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 thesend_telemetrytool 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
valuefieldsGET /predictionsreturns.
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 | |
|---|---|
| Measurement | link |
| Entity | Tag entity_id set to the link ID, for example sat-1-gs-1. |
| Fields | snr_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. |
| Cadence | One record every 15 seconds while the link is in view. |
| History | The 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.
Explore the link budget
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.
- 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
| Feature | Unit | From | Description |
|---|---|---|---|
elevation_deg | degrees | elevation_deg | Satellite elevation seen from the ground station. |
azimuth_deg | degrees | azimuth_deg | Satellite azimuth seen from the ground station. |
slant_range_km | km | slant_range_km | Distance between the satellite and the ground station. |
snr_last_observed_db | dB | snr_db | Most recent measured SNR on the link. |
downlink_freq_ghz | GHz | frequency_ghz | Downlink carrier frequency. |
bandwidth_mhz | MHz | bandwidth_mhz | Channel bandwidth. |
g_t_db_k | dB/K | g_t_db_k | Ground station figure of merit (G/T). |
eirp_dbw | dBW | eirp_dbw | Satellite effective isotropic radiated power. |
Outputs
Per horizon:
| Field | Unit | Description |
|---|---|---|
snr_db_p50 | dB | Predicted SNR. |
snr_db_p10 | dB | Lower bound of the 80% interval. |
snr_db_p90 | dB | Upper 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 | |
|---|---|
| Measurement | pool_interval |
| Entity | Tag entity_id set to the pool ID. |
| Identity tags | boundary_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. |
| Field | inlet_bytes: bytes newly offered during that ten-minute interval, not a running total. |
| Cadence | One record per ten-minute interval, timestamped on the UTC grid (:00, :10, :20, ...). |
| History | Seven 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:
| Reason | Cause |
|---|---|
incomplete_telemetry | An interval is missing in the seven-day window. |
missing_pool_identity | boundary_id, direction, or evidence_kind is missing. |
ambiguous_series | An identity tag changed within the window, so the pool has more than one history. |
uncalibrated_population | evidence_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
| Input | Unit | Description |
|---|---|---|
| History | bytes | The trailing day, matching windows on each prior day, and the seven-day mean of inlet_bytes. |
| Pool identity | none | boundary_id, direction, evidence_kind. |
as_of_utc | UTC time | Issue 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):
| Field | Unit | Description |
|---|---|---|
offered_bytes_p50 | bytes | Median forecast of bytes offered in the interval. |
offered_bytes_p10 | bytes | 10th-percentile forecast. |
offered_bytes_p99 | bytes | 99th-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.