Skip to main content

Models

Constellation OS runs versioned models against your fleet's telemetry and orbital data. Each result carries the model and feature-schema identifiers that produced it.

Every model forecasts only from data it can read for the entity you name. For each model, this page lists:

  • What to send: the telemetry you write with POST /telemetry (or the send_telemetry tool on the MCP server, for AI agents) so the model has an input.
  • What the model reads: the features it computes from that telemetry.
  • Outputs: the value fields GET /predictions returns.

In every case the entity_id tag is the ID you later pass in link_ids. A model that cannot read enough input for an entity lists it in entity_errors instead of returning a forecast.

SNR​

Forecasts a downlink's signal-to-noise ratio in dB at the 1, 3, and 5 minute horizons from the link's recent observations, pass geometry, and RF configuration.

The current release (snr-baseline-v1) is a baseline: its median is the link's last observed snr_db, carried forward to every horizon, and the interval around it widens with the horizon. It does not extrapolate a trend. Use it as a reference forecast.

What to send​

Value
Measurementlink
EntityTag entity_id set to the link ID, for example sat-1-gs-1.
Fieldssnr_db (observed SNR, dB), elevation_deg, azimuth_deg, slant_range_km, frequency_ghz, bandwidth_mhz, g_t_db_k, eirp_dbw. Send all of them in the same record.
CadenceOne record every 15 seconds while the link is in view.
HistoryThe model reads the last 4 minutes. Sparser samples still forecast, with less evidence behind them; a link with no recent samples is declined with no_telemetry or insufficient_telemetry.

Omitted geometry and RF fields are filled with fixed defaults (elevation 30°, 20 GHz, 36 MHz, G/T 20 dB/K, EIRP 50 dBW), so the forecast then describes a generic link rather than yours.

{
"measurement": "link",
"time": "2026-10-04T12:00:00Z",
"tags": { "entity_id": "sat-1-gs-1" },
"fields": {
"snr_db": 12.5,
"elevation_deg": 45.0,
"azimuth_deg": 180.0,
"slant_range_km": 1200.0,
"frequency_ghz": 20.0,
"bandwidth_mhz": 36.0,
"g_t_db_k": 20.0,
"eirp_dbw": 50.0
}
}

Then request GET /predictions?model_family=snr&link_ids=sat-1-gs-1.

Geometry and RF configuration bound what any SNR forecast can be. Drag the fields below to see how elevation, frequency, power, and rain move a clear-sky free-space budget.

25°
13.6dB SNR
Link closes: +10.6 dB over a 3 dB threshold
Slant range
1123 km
Free-space loss
179.5 dB
C/N₀
89.1 dB-Hz

A clear-sky free-space budget. The SNR model reads these same fields from your telemetry and learns what this formula misses.

What the model reads​

FeatureUnitFromDescription
elevation_degdegreeselevation_degSatellite elevation seen from the ground station.
azimuth_degdegreesazimuth_degSatellite azimuth seen from the ground station.
slant_range_kmkmslant_range_kmDistance between the satellite and the ground station.
snr_last_observed_dbdBsnr_dbMost recent measured SNR on the link.
downlink_freq_ghzGHzfrequency_ghzDownlink carrier frequency.
bandwidth_mhzMHzbandwidth_mhzChannel bandwidth.
g_t_db_kdB/Kg_t_db_kGround station figure of merit (G/T).
eirp_dbwdBWeirp_dbwSatellite effective isotropic radiated power.

Outputs​

Per horizon:

FieldUnitDescription
snr_db_p50dBPredicted SNR.
snr_db_p10dBLower bound of the 80% interval.
snr_db_p90dBUpper bound of the 80% interval.

Demand​

Forecasts a pool's offered traffic for the next hour, in ten-minute intervals, from the daily and weekly pattern in that pool's last seven days.

What to send​

Value
Measurementpool_interval
EntityTag entity_id set to the pool ID.
Identity tagsboundary_id, direction, and evidence_kind on every record, unchanged for the whole seven days. evidence_kind is public_flow_estimate for measured traffic or synthetic_offered for synthetic traffic.
Fieldinlet_bytes: bytes newly offered during that ten-minute interval, not a running total.
CadenceOne record per ten-minute interval, timestamped on the UTC grid (:00, :10, :20, ...).
HistorySeven complete days: 1,008 consecutive intervals.

The history must be complete. One missing interval declines the pool until that gap ages out of the seven-day window. A new pool therefore forecasts seven days after it starts reporting, or immediately if you backfill seven days of history; batches of up to 1,000 records per request make a backfill two requests per pool.

{
"measurement": "pool_interval",
"time": "2026-10-04T12:10:00Z",
"tags": {
"entity_id": "pool-1",
"boundary_id": "inlet",
"direction": "up",
"evidence_kind": "public_flow_estimate"
},
"fields": { "inlet_bytes": 2150000000 }
}

Then request GET /predictions?model_family=demand&link_ids=pool-1.

A pool can be declined for:

ReasonCause
incomplete_telemetryAn interval is missing in the seven-day window.
missing_pool_identityboundary_id, direction, or evidence_kind is missing.
ambiguous_seriesAn identity tag changed within the window, so the pool has more than one history.
uncalibrated_populationevidence_kind is not one the release is calibrated for.

Demand declines currently reach the API response with a null code and a generic message; until that is fixed, check the pool against this table.

What the model reads​

InputUnitDescription
HistorybytesThe trailing day, matching windows on each prior day, and the seven-day mean of inlet_bytes.
Pool identitynoneboundary_id, direction, evidence_kind.
as_of_utcUTC timeIssue time the horizons count from, on the ten-minute grid. The API aligns it for you.

Outputs​

Per pool and ten-minute horizon (10 to 60 minutes):

FieldUnitDescription
offered_bytes_p50bytesMedian forecast of bytes offered in the interval.
offered_bytes_p10bytes10th-percentile forecast.
offered_bytes_p99bytes99th-percentile forecast.

Each item also carries a demand block with the pool's identity tags, the forecast window, and a qualification_status. Forecasts from synthetic_offered history are unqualified.