CSV outputs
What they are
Flat tables for analysis in a spreadsheet, a data frame, or anything else that reads CSV. Two are produced from a scenario: an ephemeris table and a contact table.
They are the most convenient export and the least self-describing, and the second half of that sentence is the important one.
Ephemeris CSV
curl -s http://127.0.0.1:8080/scenarios/scenario-gj/ephemeris.csvsatellite_id,satellite_name,time_s,x_km,y_km,z_km,vx_km_s,vy_km_s,vz_km_s
gj-s0-p00-sat00,gj-s0-p00-sat00,0,6928.137,0,0,0,4.564820232396907,6.057721051030392
gj-s0-p00-sat00,gj-s0-p00-sat00,600,5486.340609293676,2546.122041969697,3378.8180710094707,-4.631912845789708,3.614847485712905,4.79706463686287
gj-s0-p00-sat00,gj-s0-p00-sat00,1200,1761.0483556547456,4032.5105450645924,5351.322236957664,-7.335955263172513,1.1603219110883758,1.539799183522527Real output, elided after three rows.
| Parameter | Type | Unit | Default | Required | Description |
|---|---|---|---|---|---|
satellite_id | string | n/a | — | No | Stable identifier. Rows are grouped by satellite, then ordered by time. |
satellite_name | string | n/a | — | No | Display name. Equal to the identifier for generated constellations. |
time_s | number | s | — | No | Elapsed seconds from the scenario start. NOT an absolute epoch. See below. |
x_km, y_km, z_km | number | km | — | No | Position in the scenario frame. |
vx_km_s, vy_km_s, vz_km_s | number | km/s | — | No | Velocity. |
Kilometers, like everything except CZML, which uses meters.
The file carries no provenance
This CSV has no frame, no time system, and no absolute epoch. time_s is
elapsed seconds from a scenario start that lives somewhere else entirely.
A row reading 600, 5486.34, 2546.12, 3378.82 is meaningless on its own. It
does not say which frame those axes are, when the scenario began, or which
propagation model produced it.
Fetch the scenario metadata alongside any CSV you intend to keep:
curl -s http://127.0.0.1:8080/scenarios/scenario-gj > scenario.json
curl -s http://127.0.0.1:8080/scenarios/scenario-gj/ephemeris.csv > ephemeris.csv{
"scenario_id": "scenario-gj",
"start_time": "2026-01-01T00:00:00Z",
"duration_seconds": 21600.0,
"step_seconds": 60.0,
"model": "two_body",
"satellite_count": 60,
"sample_count": 361
}start_time is what converts time_s into an absolute epoch, and model is
what tells a reviewer how much to trust the numbers. Without both, the CSV is a
table that cannot be reproduced or checked, which is precisely what makes a
result impossible to audit later.
If you need a self-describing file, use CCSDS OEM instead. It declares its frame, time system, and span in the file.
Contacts CSV
curl -s http://127.0.0.1:8080/scenarios/scenario-demo/contacts.csvContact records exist only where a link analysis produced them. Requesting them from a plain propagation returns an explicit error rather than an empty file:
{
"error": {
"code": "invalid_request",
"message": "scenario `scenario-gj` has no contacts (run a link analysis to produce them)"
}
}That error is the right behavior and worth relying on. An empty CSV would be indistinguishable from a constellation that genuinely never made contact, and a scheduling tool consuming it would silently plan nothing.
Run POST /link first; it registers its own scenario,
from which contacts can be fetched.
Reading them
Rows are grouped by satellite and ordered by time within each group, so a naive
sort by time_s interleaves satellites. Group first.
For a large constellation these files are big: one row per satellite per step, so 60 satellites at 361 steps is 21,660 rows for six hours. Halving the step doubles it.
See also
- CCSDS OEM when the recipient needs frame and time system in the file.
- Scenario endpoints for the metadata to keep alongside.
main (pre-release)