Skip to Content
ReferenceCLIwaveform doppler-apply

orbitforge waveform doppler-apply

Synopsis

orbitforge waveform doppler-apply \ --iq <PATH> --sample-rate-hz <HZ> \ --constellation <PATH> --satellite <ID> --site <SPEC> \ --output <PATH> [OPTIONS]

Description

Takes a clean IQ capture and imposes the Doppler shift and propagation delay that a real satellite pass would produce, by simulating the pass in process and sampling its geometry.

This is what turns a transmit-side reference from waveform generate into something worth testing a receiver against. A demodulator that works on a static signal and fails on a real pass has failed at carrier tracking, and this is how you find that out before flight.

Options

ParameterTypeUnitDefaultRequiredDescription
--iqpathn/a—YesInput IQ capture, cf32 format.
--sample-rate-hzfloatHz—YesCapture sample rate. Used to convert the time-varying shift into a per-sample rotation.
--constellationpathn/a—YesConstellation JSON. The pass is simulated in process.
--satellitestringn/a—YesSatellite identifier within the constellation.
--sitestringn/a—YesGround site as `name,lat_deg,lon_deg,alt_km`.
--outputpathn/a—YesOutput IQ path.
--carrier-hzfloatHz2200000000NoCarrier frequency the Doppler is scaled to. Shift is proportional to carrier, so this matters.
--startstringRFC 3339 UTC2026-01-01T00:00:00ZNoScenario start epoch.
--duration-hoursfloath6NoSpan searched for contacts.
--step-secondsfloats10NoSimulation step, setting profile sampling fidelity.
--min-elevation-degfloatdeg5NoMinimum elevation defining a contact.
--contactintegerindex—NoContact index in time order. Defaults to the highest-elevation contact.
--offset-sfloats0NoSeconds into the contact at which the capture starts.
--doppler-onlyflagn/a—NoApply Doppler without the propagation delay.

Worked example

orbitforge waveform doppler-apply \ --iq iq.bin --sample-rate-hz 32000 \ --constellation demo.json \ --satellite demo-s0-p00-sat00 \ --site "Madrid,40.43,-3.7,0.6" \ --output iq_dop.bin
Contacts for demo-s0-p00-sat00 over Madrid: [0] t = 1360..1850 s, peak elevation 16.5 deg, Doppler +37.3..-37.3 kHz [1] t = 7340..7930 s, peak elevation 41.9 deg, Doppler +46.8..-46.9 kHz [2] t = 13320..13880 s, peak elevation 31.4 deg, Doppler +45.6..-45.3 kHz Applied Doppler + delay to 65680 samples (Doppler +46.8 kHz at capture start) -> iq_dop.bin

Reading the contact list

Three passes were found, and contact 1 was selected automatically because it has the highest peak elevation at 41.9 degrees.

ContactDurationPeak elevationDoppler range
0490 s16.5 deg+37.3 to -37.3 kHz
1590 s41.9 deg+46.8 to -46.9 kHz
2560 s31.4 deg+45.6 to -45.3 kHz

The sign reversal is the whole point

Every contact starts positive and ends negative. The satellite approaches, so the received frequency is high; it recedes, so the frequency is low; and it passes through zero at closest approach.

fd=−r˙cfcf_d = -\frac{\dot{r}}{c} f_c

where r˙\dot{r} is range rate. At 2.2 GHz a low Earth orbit pass sweeps roughly 90 kHz peak to peak, and the steepest part of that sweep happens exactly at closest approach, when the signal is strongest and a receiver is most likely to be tracking.

A higher pass has a larger Doppler swing, not a smaller one. Contact 1 at 41.9 degrees sweeps +46.8 to -46.9 kHz; contact 0 at 16.5 degrees only sweeps +37.3 to -37.3 kHz.

A high pass brings the satellite closer and more directly overhead, so the line-of-sight velocity changes faster and through a wider range. The best pass for link margin is therefore the hardest pass for carrier tracking, which is a trap worth knowing about when you choose a test case.

Delay as well as Doppler

By default both Doppler and propagation delay are applied. Delay changes with range over the pass, which stretches and compresses the sample timeline, so a receiver must track symbol timing as well as carrier frequency.

--doppler-only omits the delay, which is useful for isolating a carrier-tracking problem from a timing-recovery problem. Both together is the realistic case.

Choosing the contact and the offset

FlagUse
--contactSelect a specific pass by time order rather than taking the highest-elevation one
--offset-sStart the capture partway into the pass

--offset-s matters because the hardest moment is not the start. Setting it to place the capture across closest approach exercises the receiver where Doppler rate is highest, which is where carrier tracking usually breaks.

Profile fidelity

--step-seconds defaults to 10 seconds, and the profile is interpolated between samples. That is adequate for a pass lasting several minutes, because range rate varies smoothly. Reduce it if you need the Doppler rate through closest approach represented precisely, since that is where the curvature is greatest.

A complete receiver test

# 1. Generate a clean reference. orbitforge waveform generate --standard dvbs2 --modcod qpsk-1/2 --iq iq.bin # 2. Impose a real pass. orbitforge waveform doppler-apply \ --iq iq.bin --sample-rate-hz 32000 \ --constellation demo.json --satellite demo-s0-p00-sat00 \ --site "Madrid,40.43,-3.7,0.6" --output iq_dop.bin # 3. Feed iq_dop.bin to the receiver under test.

Keeping the steps separate means a failure is attributable: if the receiver handles iq.bin and fails on iq_dop.bin, the problem is Doppler or timing rather than demodulation.

See also

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