Skip to Content
GuidesAnalyzingDoppler and orbital elements

Doppler and orbital elements

What you will accomplish

A Doppler profile you can hand to a receiver designer, and an osculating-element series you have checked against theory rather than assumed.

Both come from the API rather than the CLI.

Prerequisites

  • The API running, and a constellation registered with it.
  • Access windows and pointing, whose range rate is the same quantity seen from the other side.

Doppler over a pass

curl -s -X POST localhost:8080/analysis/doppler \ -H 'content-type: application/json' \ -d '{ "constellation_id": "ph1", "duration_hours": 2, "step_seconds": 30, "satellite_id": "ph1-s0-p00-sat00", "frequency_hz": 12e9, "ground_station": { "id": "nyc", "name": "New York", "latitude_deg": 40.7, "longitude_deg": -74.0, "altitude_km": 0.01 } }'
{ "satellite_id": "ph1-s0-p00-sat00", "ground_station_id": "nyc", "frequency_hz": 12000000000.0, "start": "2026-01-01T00:00:00Z", "samples": [ { "time_s": 0.0, "range_km": 5347.98, "range_rate_km_s": -6.3357, "doppler_hz": 253601.72 } ] }

The sign convention is worth fixing in your head immediately: range rate negative means closing, and closing gives positive Doppler. A satellite approaching the station arrives high in frequency.

Every sample is returned, including the ones through the Earth

The response has no elevation field and applies no visibility gate. Over this 2-hour window at 30 seconds, all 241 samples are returned, and 191 of them are for a satellite below the horizon.

Range runs from 811 km to 13297 km. For a 550 km circular orbit the slant range at zero elevation is about 2705 km, so anything beyond that is geometry computed straight through the planet.

This matters less for magnitudes than you might expect, and much more for interpretation.

PopulationDoppler extremesSpan
All 241 samples-263.6 to +263.4 kHz526.9 kHz
Above the horizon (50)-261.9 to +261.5 kHz523.4 kHz
Above 10 degrees (30)-244.4 to +242.9 kHz487.3 kHz

Sizing a receiver’s search range from the raw series overshoots by 8 percent against a 10-degree mask. That is a small error and would not by itself break a design.

The interpretation error is the large one: 79 percent of that series is not an observation. Over these 2 hours the satellite rises above 10 degrees exactly twice, for 15 minutes in total. Plot the series unfiltered and you get a smooth continuous Doppler curve for a satellite that was in view for one eighth of the window. Anything time-dependent, such as scheduling an acquisition or estimating how long the receiver must hold lock, is wrong by far more than 8 percent.

Filter by elevation from a platform-access run over the same window and step before using the series.

What the receiver actually needs

Two numbers, from the same geometry:

  • Frequency uncertainty, roughly plus or minus 245 kHz at 12 GHz above a 10-degree mask. This sets the acquisition search range.
  • Doppler rate, which sets how fast the loop must track. From a 1-second access run over the highest pass, the peak is 3464 Hz/s at 12 GHz.

Doppler scales linearly with carrier frequency, so the same orbit at 2 GHz gives about one sixth of both figures. Scale rather than re-running, but state the frequency you scaled from.

A 30-second step cannot resolve a Doppler rate. Range rate swings through its full excursion in a couple of minutes near closest approach, so use 1-second steps for any rate figure, exactly as for antenna slew rates.

Osculating elements

curl -s -X POST localhost:8080/analysis/elements \ -H 'content-type: application/json' \ -d '{ "constellation_id": "ph1", "duration_hours": 2, "step_seconds": 30, "propagation_model": "j2", "sample_index": 240 }'
{ "satellite_id": "ph1-s0-p00-sat00", "semi_major_axis_km": 6928.137, "eccentricity": 0.0, "inclination_deg": 53.0, "raan_deg": 359.6259, "argument_of_perigee_deg": 0.0, "true_anomaly_deg": 91.9261, "perigee_altitude_km": 550.0, "apogee_altitude_km": 550.0, "period_minutes": 95.6499 }

sample_index selects one sample from the propagated series, so the window and step determine which instant you get. Index 240 at 30-second steps is 7200 seconds in.

The API spells the model two_body with an underscore. The CLI spells it two-body with a hyphen. Passing the CLI spelling to the API returns a clear error naming the valid values:

unknown propagation_model `two-body` (expected `two_body`, `j2`, `sgp4`, `numerical`, or `ephemeris`)

Verify the propagator instead of trusting it

RAAN moved from 0 to 359.6259 degrees in 7200 seconds, which is -4.489 degrees per day. That is a claim the tool is making, and it has a closed form to check against.

For a near-circular orbit the J2 secular nodal regression is

Ω˙=−32J2(Rea)2ncos⁡i\dot{\Omega} = -\frac{3}{2} J_2 \left(\frac{R_e}{a}\right)^2 n \cos i

With J2=1.08263×10−3J_2 = 1.08263 \times 10^{-3}, Re=6378.137R_e = 6378.137 km, a=6928.137a = 6928.137 km, and i=53i = 53 degrees:

SourceNodal regression
Closed form-4.489 deg/day
Measured from the j2 model-4.489 deg/day

They agree to four decimal places. Running two_body over the identical window leaves RAAN at exactly 0.0000 degrees, which is the control: without J2 there is no regression to find.

This check takes two requests and closes off a whole category of doubt. Do it once for any model you are about to quote numbers from.

The same comparison also exposes a smaller effect. At the same sample, true anomaly is 91.6472 degrees under two_body and 91.9261 degrees under j2. J2 perturbs the along-track rate as well as the node, and over weeks that difference is what moves a satellite out of its slot.

Why nodal regression is the number to check

It sets whether a Sun-synchronous orbit holds its local time, how quickly planes drift apart, and how a constellation’s geometry ages. At -4.489 deg/day, a plane walks 31 degrees in a week. A design verified over 6 hours has seen none of that, which is the argument made in Synthesize from requirements arriving from a different direction.

Checklist

  1. Did you gate the Doppler series by elevation before using it?
  2. Are you quoting a Doppler rate from a step fine enough to resolve it?
  3. Is the carrier frequency stated alongside every Doppler figure?
  4. Did you check the model’s secular rates against the closed form?
  5. Are you using the API spelling of the model name, not the CLI one?

Next steps

Question? Give us feedbackDocuments Varaha Constellation Designer main (pre-release)
Last updated on