Aller au contenu

omnilab/jpl

Omnilab JPL: an asteroid or a comet by its number, designation or name, from NASA/JPL's Small-Body Database API - its orbit (elements at their epoch with their uncertainties, period, MOID), physical parameters (absolute magnitude, diameter, albedo, rotation period, density, spectral types), NEO and PHA flags, discovery and IAU citation, close approaches to the Earth and Sentry's impact risk - and a search by name or designation (SBDB Query API), no key needed.

Étage
Fournisseur · Omnilab
Version
1.x
Suite de conformité
non branchée
Vérifié en réel
Son README ne le dit pas
Licence
MIT
composer require omnilab/jpl:1.x-dev

NASA/JPL's Small-Body Database, through the SSD/CNEOS API service (https://ssd-api.jpl.nasa.gov/: sbdb.api, sbdb_query.api), for omnisense/omnilab: asteroids and comets by number, designation, name or SPK-ID - orbit, physical parameters, NEO and PHA flags, close approaches to the Earth, Sentry's impact risk - as entities; a search. No key.

Installation

composer require omnilab/jpl
use Omnilab\Jpl\JplSourceFactory;

$jpl = (new JplSourceFactory($httpClient))->create([
    'mailto' => 'support@glitchr.io',  // recommended: JPL asks a User-Agent with a contact
    'synonyms' => 20,                  // optional
    'throttle' => 0.2,                 // optional
]);

With the Symfony bundle, the factory is registered as jpl when the bundle knows it; otherwise register JplSourceFactory as a service (it is autoconfigured as a SourceFactoryInterface).

Credentials

None: the SSD/CNEOS API service takes no key. JPL's fair use policy asks for an application-specific User-Agent with a contact (give mailto), one request at a time, results cached; it does not let a web page call it from the browser (CORS): call it from the server.

Options

The factory's options, with the defaults it applies (JplSourceFactory):

Option Type Default Required Effect
synonyms int 20 no how many alternate designations to keep; 0: none
now ?string null no the moment the next close approach is reckoned from (a DateTimeImmutable or a date string, UTC), for tests; null: now
mailto ?string null no a contact address, in the User-Agent: JPL's fair use asks an application-specific one
user_agent ?string null no the whole User-Agent, in place of the one made from mailto
throttle float 0.2 no seconds left between two calls: JPL asks one request at a time
base_uri string https://ssd-api.jpl.nasa.gov/ no the API the source calls

Capabilities

capabilities() answers entity, search; an operation it does not answer throws NotSupportedException before any call.

Capability Supported
entity yes a number (433), a designation (2004 MN4, 1P, C/2020 F3), a name, an SPK-ID, jpl.sbdb:, an SBDB Lookup page: one call with every section; a name two bodies share is null; another scheme refused before any call
search yes the names that hold the text, whatever its case, or the designation that is it, 100 a page at most, paged by offset, with a total; a domain other than astronomy answers an empty page
series no close approaches and Sentry are not series (no place on the Earth): read into the body's properties
products no
occurrences no
datasets no
jobs no
open no
structure no
cross_references no its SPK-ID as a second jpl.sbdb identifier; the MPC names the body by the same designation
forecast no
historical no
area no
station no
cloud_cover no

Which API

The SSD/CNEOS API service (https://ssd-api.jpl.nasa.gov/): sbdb.api (version 1.3) for one body - asked with every section it can add (phys-par, full-prec, alt-des, discovery, cd-epoch, cd-tp, ca-data with ca-body=Earth, vi-data), so one call gives the orbit, the physical parameters, the discovery, the close approaches and Sentry's virtual impactors - and sbdb_query.api (1.0) for the search. Without full-prec the SBDB rounds every value to three digits (Eros's eccentricity 0.223): it is always asked.

Close approaches and Sentry: not a Series

cad.api lists close approaches (date, distance, velocity of each body passing near a planet in a period) and sentry.api the impact risk of the bodies Sentry follows. Neither is a Series: a series is one variable at a place on the Earth over a time (Series::$place is a WGS 84 Point or BBox), and an approach list is a set of bodies, each at its own date, with no place. So they are not added as series; the SBDB gives the same data for one body, read into its properties: the close approaches to the Earth (ca-data, 1900-2200: their number, the closest, the next from now) and Sentry's summary computed from its virtual impactors (vi-data: their number, the cumulative impact probability, the cumulative and the highest Palermo scale, the highest Torino scale - for Bennu 157, 5.717e-4, -1.40 and -1.58, the figures sentry.api's own summary gave the same day).

Calls

Method Reads JPL
entity() a number (433), a designation (2004 MN4, 1P, C/2020 F3), a name (Eros), an SPK-ID (20000433), jpl.sbdb:433, an SBDB Lookup page sbdb.api?sstr= with the sections above
search() Query::$text: the names that hold it, whatever its case, or the designation that is it; a domain other than astronomy answers nothing sbdb_query.api?fields=...&sb-cdata={"OR":["name|RE|[Ee][Rr][Oo][Ss]","pdes|EQ|eros"]}&limit=&limit-from=

sstr that matches several bodies answers a list (code 300: "Halley" is 1P/Halley and the asteroid 2688 Halley): entity() gives null rather than a guess - ask 1P or 2688. An unknown body is answered 200 with "specified object was not found": null. An identifier of another base is refused before any call. The search's regular expression is case-sensitive at JPL: each letter is asked in both cases; it takes 4 to 7 s, and pages by limit-from (Page::$next is the next offset, total the SBDB's count), in the SBDB's order (numbered bodies first, by number).

What an entity holds

id (its primary designation: jpl.sbdb:433, jpl.sbdb:1P, jpl.sbdb:C/2020 F3), label (the SBDB's short name: 433 Eros, 1P/Halley), domain astronomy, kind asteroid or comet, description (the IAU citation of its name, when it has one: Bennu's), synonyms (its full name, then its alternate designations), identifiers (its SPK-ID as a second jpl.sbdb identifier, 20000433), url (its SBDB Lookup page), and these properties, numbers with their unit and 1-sigma uncertainty as the SBDB gives them:

Key SBDB Value
neo, pha object.neo, object.pha boolean
orbit_class, orbit_class_code object.orbit_class text: Amor, AMO
epoch, epoch_jd orbit.epoch_cd, orbit.epoch text (TDB), Julian date
eccentricity, semi_major_axis, perihelion_distance, aphelion_distance e, a, q, ad number, au
inclination, ascending_node, argument_of_perihelion, mean_anomaly i, om, w, ma deg
orbital_period, mean_motion per, n d, deg/d
perihelion_time, perihelion_time_jd tp_cd, tp text (TDB), Julian date
moid, moid_jupiter, tisserand_jupiter orbit.moid, moid_jup, t_jup au, au, number
condition_code, data_arc, observations, first_observation, last_observation the orbit's quality and data number, d, number, date, date
absolute_magnitude, slope_parameter H, G mag, number
diameter, extent, gm, density diameter, extent, GM, density km, text, km³/s², g/cm³
rotation_period, rotation_acceleration, pole rot_per, rot_acc, pole h, rad/d², text
albedo, thermal_inertia albedo, I number, J/m²/s^0.5/K
color_b_v, color_u_b, color_i_r BV, UB, IR mag
spectral_type_tholen, spectral_type_smass spec_T, spec_B text
comet_total_magnitude, comet_nuclear_magnitude and their slopes, comet_phase_coefficient M1, K1, M2, K2, PC mag, number
discovery_date, discovered_by, discovery_site discovery text
earth_approaches; closest_earth_approach, next_earth_approach and their _distance, _velocity ca_data (Earth) number; text (TDB), au, km/s
sentry_impacts, sentry_impact_probability, sentry_palermo_cumulative, sentry_palermo_max, sentry_torino_max vi_data number

Another physical parameter the SBDB adds is kept as physical_ and its name. A search result holds the flags, the class code, the main elements, the MOID, H, the diameter, the albedo and the rotation period: entity() reads the rest.

The Collector: Eros from Wikidata

Wikidata's JPL Small-Body Database id (P716) is the SPK-ID, which the SBDB reads as it reads a designation. Against the real services on 2026-10-15:

$collector->collect('wikidata', 'Q16711', 1, ['jpl']);

2 calls in 1 s - wbgetentities for Q16711, then sbdb.api?sstr=20000433 - and sources = wikidata, jpl:

diameter          16.84 ± 0.06 km [jpl]
orbital_period    643.1151986547 ± 0.000000104 d [wikidata]  643.1963890928 ± 0.0000001005 d [jpl]
rotation_period   5.27 h [jpl]
albedo            0.25 ± 0.06 [jpl]
absolute_magnitude 10.4 mag [jpl]
neo               yes [jpl]

The two periods differ in their sixth digit: Wikidata's comes from an older orbit solution.

What the SBDB does not give here

The covariance, the non-gravitational parameters (model_pars: kept by the SBDB, not read), the radar astrometry, the satellites, the approaches to other bodies than the Earth, each virtual impactor's date and energy, the planets and moons (not small bodies), the Horizons ephemerides and the other SSD/CNEOS APIs (Scout, NHATS, fireballs, cad.api and sentry.api as lists: see above) are not read.

Example

<?php

require __DIR__.'/vendor/autoload.php';

use Omnilab\Jpl\JplSourceFactory;
use Omnilab\Source\Query;
use Symfony\Component\HttpClient\HttpClient;

$jpl = (new JplSourceFactory(HttpClient::create()))->create(['mailto' => 'you@example.org', 'synonyms' => 3]);

$bennu = $jpl->entity('Bennu');
echo $bennu->label, ' (', $bennu->id, '): NEO ', $bennu->property('neo'), ', PHA ', $bennu->property('pha'), ', diameter ', $bennu->property('diameter'), "\n";
echo '  closest Earth approach ', $bennu->value('closest_earth_approach'), ' at ', $bennu->property('closest_earth_approach_distance'), "\n";
echo '  Sentry: ', $bennu->value('sentry_impacts'), ' virtual impactors, probability ', $bennu->value('sentry_impact_probability'), ', Palermo max ', $bennu->value('sentry_palermo_max'), "\n";
var_dump($jpl->entity('Halley'));   // two bodies: neither

$page = $jpl->search(new Query('eros', limit: 3));
printf("%d bodies hold \"eros\"; the first %d: %s\n", $page->total, count($page), implode(', ', array_map(static fn ($b) => $b->label, $page->items)));
101955 Bennu (jpl.sbdb:101955): NEO yes, PHA yes, diameter 0.48444 ± 0.0003 km
  closest Earth approach 2135-Sep-25 07:49 at 0.0013639272 au
  Sentry: 157 virtual impactors, probability 0.0005717, Palermo max -1.58
NULL
13 bodies hold "eros"; the first 3: 433 Eros, 947 Monterosa, 1943 Anteros

(as answered on 2026-10-27: three calls, 0.2 s apart; "Halley" names two bodies, the SBDB's code 300: null)

# config/packages/omnilab.yaml
omnilab:
    sources:
        jpl: { factory: jpl, options: { mailto: '%env(OMNILAB_MAILTO)%', synonyms: 20 } }

Errors

Every call goes through HttpSource: a 404 or a 410 (and a 204) is null - an empty page for a list -; a 429 is a RateLimitedException (retryAfter from Retry-After); a 5xx, a time-out or a host that does not answer an UnavailableException; any other 4xx a ProviderException with its status and what the service said; an answer that is not JSON a ProviderException, a web page answered for JSON an UnavailableException.

The SBDB's own: "specified object was not found" (code 200) and several matches (code 300, a list) are null; any other code with a message a ProviderException with it. Before any call: an identifier of another scheme (NotSupportedException).

Verified, and not

Against the real service on 2026-10-15: 433 Eros by its number, its name and its SPK-ID; 101955 Bennu (a PHA followed by Sentry: its summary checked against sentry.api); 99942 Apophis by 2004 MN4, 99942 and Apophis (no longer on Sentry's list); 3200 Phaethon; 1P/Halley and C/2020 F3 (NEOWISE), comets; "Halley" (code 300, two bodies); an unknown name; the searches for "eros" (13 bodies, offsets 0 and 3) and an unknown text; cad.api (to decide against a series); the Collector's walk above. The fixtures in Tests/Fixtures/ are its answers.

Again on 2026-10-27: Bennu (its closest Earth approach in 2135, Sentry's 157 virtual impactors), "Halley" null, the search for "eros" (13 bodies; the example above).

Not verified: JPL's answer to a client that calls too fast (no 429 met; the source calls one at a time, 0.2 s apart); a body asked by a planet's satellite designation.

Responsibility

This package is an independent, unofficial client: it is not affiliated with, endorsed by or sponsored by NASA, JPL or Caltech. The SSD/CNEOS API service is free under JPL's fair use policy: only the requests a use needs, one at a time, results cached, an application-specific User-Agent with a contact, as the README's Terms of use says. Whoever runs the package is responsible for complying with them and for any use made of what it returns: see the README's Responsibility section.

Proposer une modification docs/index.md