Python Quick Start¶
Using C?
This page is Python. To build a C program against the library, go to
the C Quick Start: jbx get-doppler
gets the library and headers in one command, and it builds with plain
cc, CMake, or pkg-config.
Get it!¶
Note
Isolate your install from system python with a virtual environment!
Debian and Ubuntu refuse a pip install outside one, and ship the module
that makes one as a separate package:
The wheel bundles all native dependencies — no system libraries required. Everything under Signal processing and Streaming below runs against this install alone; the C transmitter example binary needs Build from source, called out at each point it applies.
Optional extras
pip install "doppler-dsp[specan]" # terminal spectrum analyzer
pip install "doppler-dsp[specan-web]" # live spectrum analyzer web UI
pip install "doppler-dsp[cli]" # compose / Dopplerfile pipeline CLI
See Install → Python for the full extras table.
Or pull the container image
Every release publishes a ready-to-run image with the cli and
specan-web extras pre-installed — doppler, doppler-fir,
doppler-source, doppler-specan, and wfmgen are all on PATH:
docker pull ghcr.io/doppler-dsp/doppler:latest
docker run --rm ghcr.io/doppler-dsp/doppler wfmgen --help
Built for both linux/amd64 and linux/arm64. See
Docker for details.
Signal processing¶
Every object is a thin, stateful wrapper over the C core: construct it once, then stream blocks through it. Each example below is self-contained — copy-paste any one of them on its own.
LO — generate a complex tone¶
from doppler.source import LO
lo = LO(0.25) # normalised frequency: 0.25 → Fs/4
iq = lo.steps(8)
print(iq)
# [ 1.+0.j 0.+1.j -1.+0.j 0.-1.j ...]
For modulated waveforms, multi-segment scenes, and file/stream output, see Waveform Generator (wfmgen) below.
FFT¶
from doppler.spectral import FFT
import numpy as np
x = (np.random.randn(1024) + 1j * np.random.randn(1024)).astype(np.complex64)
fft = FFT(1024)
X = fft.execute_cf32(x) # complex64 in → complex64 out (~2× faster than f64)
print(f"FFT: {X.shape[0]} complex64 bins")
FIR filter¶
from doppler.filter import FIR
from doppler.spectral import kaiser_window, kaiser_beta_for_sidelobe
import numpy as np
x = (np.random.randn(1024) + 1j * np.random.randn(1024)).astype(np.complex64)
n_taps, cutoff = 63, 0.05 # cutoff: fraction of fs
m = np.arange(n_taps) - (n_taps - 1) / 2
taps = 2 * cutoff * np.sinc(2 * cutoff * m) # ideal windowed-sinc lowpass
w = np.zeros(n_taps, dtype=np.float32)
kaiser_window(w, kaiser_beta_for_sidelobe(60.0)) # 60 dB sidelobe target
taps = (taps * w).astype(np.float32)
fir = FIR(taps)
y = fir.execute(x)
print(f"filtered {len(y)} samples through a {len(taps)}-tap FIR")
The walkthrough above spells out the windowed-sinc method by hand; for real
use, doppler.filter.design_lowpass does the same design in one call —
n_taps sized automatically from the requested band edges/attenuation, no
scipy dependency (see Filter design helpers).
Resample¶
RateConverter picks the cheapest cascade (halfband / CIC / polyphase) for
the rate you ask for — no filter design required:
from doppler.resample import RateConverter
import numpy as np
x = (np.random.randn(1024) + 1j * np.random.randn(1024)).astype(np.complex64)
rc = RateConverter(0.5) # 2:1 decimation → auto-selects a halfband stage
y = rc.execute(x) # 1024 → 512 samples
print(f"resampled {len(x)} -> {len(y)} samples")
Streaming¶
Doppler streams IQ data over NATS. Transmit and receive on the same machine or across a network — the API is identical. Either side needs a running NATS server (see Run a NATS broker) to connect to.
Publisher (Python)¶
from doppler.stream import Publisher, CF32
import numpy as np
pub = Publisher("nats://127.0.0.1:4222/iq", CF32) # sample_type must match samples' dtype
samples = np.ones(1024, dtype=np.complex64)
pub.send(samples, sample_rate=1e6, center_freq=2.4e9)
print(f"sent {len(samples)} samples")
Subscriber (Python)¶
from doppler.stream import Subscriber
sub = Subscriber("nats://127.0.0.1:4222/iq")
samples, hdr = sub.recv() # (ndarray, header dict) -- not an object with attributes
print(f"Received {len(samples)} samples @ {hdr['sample_rate']/1e6:.1f} MHz")
C transmitter → Python subscriber¶
The C examples are not in the pip wheel: they are built from a clone of the repository, once (all it needs is a C compiler — see Build from source):
git clone --branch v0.81.0 https://github.com/doppler-dsp/doppler
cd doppler
make # builds ./build/native/examples/transmitter, receiver, etc.
Then mix and match, from that clone:
# Terminal 1 (transmitter takes ci32 or cf64 — not cf32)
./build/native/examples/transmitter nats://127.0.0.1:4222/iq cf64
# Terminal 2 (Python)
python - <<'EOF'
from doppler.stream import Subscriber
sub = Subscriber("nats://127.0.0.1:4222/iq")
while True:
samples, hdr = sub.recv()
print(f"seq={hdr['sequence']} samples={len(samples)}")
EOF
Waveform Generator (wfmgen)¶
No extra required
wfmgen ships in the base pip install doppler-dsp wheel — no optional
extra needed.
One engine generates a single waveform, a multi-segment JSON scene, or a live stream — the CLI and the Python API produce byte-identical output:
wfmgen --type qpsk --snr 12 --count 100000 -o capture.cf32 # a single waveform
wfmgen json-template scenario.json # write an example scene to edit
wfmgen --from-file scenario.json -o scenario.cf32 # play the multi-segment scene
wfmgen --type qpsk --continuous --realtime --output nats://127.0.0.1:4222/iq # stream to NATS
A scene is a JSON file of segments; json-template writes a working one
(a tone, a QPSK burst, and two sources summed) for you to edit.
The same waveform from Python:
from doppler.wfm import Synth
synth = Synth(type="qpsk", fs=1e6, snr=12.0, snr_mode="esno", sps=8, seed=1)
iq = synth.steps(4096) # complex64 ndarray
print(f"generated {len(iq)} QPSK samples")
See Waveform Generator (wfmgen) for scenes,
BLUE/SigMF, streaming, and the Plan bit-exact sweep cache.
Spectrum analyzer¶
doppler-specan opens a live FFT display in your terminal or browser.
Requires the specan or specan-web extra
Demo mode (no hardware needed):
Browser UI:
The web UI is served at http://127.0.0.1:8765 by default.
See Spectrum Analyzer for configuration options.
Pipeline CLI¶
doppler compose wires blocks into a processing pipeline defined in a
YAML file.
doppler compose init tone fir specan --name my_pipeline --out my_pipeline.yaml
doppler compose up my_pipeline.yaml
doppler ps
doppler logs my_pipeline
See CLI & Pipelines and Dopplerfile for writing custom blocks.
Build from source¶
Only need the C library itself (headers + libdoppler.a/.so, no
examples, no Rust FFI, no toolchain)? jbx get-doppler —
see Get it! above — is faster. This section is for the examples,
the Rust FFI bindings, running the test suite, or contributing.
Don't have jbx yet?
make install-deps bootstraps it for you (installs system build
dependencies too). Or by hand: Get jbx.
git clone --branch v0.81.0 https://github.com/doppler-dsp/doppler
cd doppler
make install-deps # bootstrap jbx (if needed) + install system deps
make # C library + examples
make pyext # Python extensions
make test-all # C, Python and example test suites (Rust: make test-rust)
You'll need a C compiler — your system's default one is enough, no C++
toolchain is required anywhere in the build (the core library and the
optional stream component, which vendors nats.c, are both pure C99).
Installing system deps by hand instead
make install-deps reads bootstrap.toml,
the single source of truth for doppler's system deps, so it stays in
sync automatically. To install them yourself instead:
The C library builds natively with clang or clang-cl (Visual
Studio's C++ Clang tools for Windows): see
Build from Source → Windows for the
commands and what the Windows build leaves out, or download the
pre-built zip: Install the C library → Windows.
For Python, pip install doppler-dsp installs the Windows wheel,
the same package as on Linux and macOS.
See Build from Source for CMake options, Docker, and platform-specific notes.
Next steps¶
- Architecture — design overview and layer diagram
- Examples: C · Streaming
- API reference — full C and Python API docs
- Waveform Generator (wfmgen) — scenes, BLUE/SigMF, NATS streaming, and the Plan sweep cache
- Spectrum Analyzer — specan configuration
- CLI & Pipelines — compose and Dopplerfile