Build and Installation Guide
See README for an overview and quick examples.
Quick build
make
Produces the following artifacts in build/ (extensions differ by platform):
| Artifact | Linux/macOS | Windows (MinGW) | Description |
|---|---|---|---|
| Shared Library | libdoppler.so/dylib |
libdoppler.dll |
DSP + streaming |
| Static Library | libdoppler.a |
libdoppler.a |
Static link (no runtime dep) |
| Python extension | fft/_fft*.so |
fft/_fft*.pyd |
FFT |
| Python extension | buffer/_buffer*.so |
buffer/_buffer*.pyd |
Lock-free ring buffer |
| Python extension | stream/_stream*.so |
stream/_stream*.pyd |
ZMQ streaming (vendored libzmq) |
| Python extension | nco/_nco*.so |
nco/_nco*.pyd |
NCO |
| Python extension | accumulator/_accumulator*.so |
accumulator/_accumulator*.pyd |
Accumulator |
| Python extension | delay/_delay*.so |
delay/_delay*.pyd |
Delay line |
| Python extension | resample/_resamp*.so |
resample/_resamp*.pyd |
Polyphase resampler |
| Python extension | resample/_resamp_dpmfs*.so |
resample/_resamp_dpmfs*.pyd |
DPMFS resampler |
| Python extension | resample/_hbdecim*.so |
resample/_hbdecim*.pyd |
Halfband decimator |
| C examples | transmitter, receiver, … |
transmitter.exe, … |
Streaming and DSP demos |
| Rust examples | ffi/rust/target/…/nco_demo, … |
same | NCO, FFT, SIMD demos |
And the Python packages are in dist/:
doppler_dsp-*.whl, doppler_specan-*.whl, doppler_cli-*.whl
(plus *.tar.gz sdists)
Targets
| Target | Description |
|---|---|
make |
Configure + build (Release by default) |
make test |
Run CTest suite |
make test-all |
Run all test suites (C + Python + Rust) |
make pyext |
Build Python extensions into python/dsp/doppler/ |
make install |
Install headers + libs to system (default /usr/local) |
make python-test |
Run pytest |
make rust-test |
Run Rust FFI tests (single-threaded) |
make rust-examples |
Build Rust examples and list their paths |
make docker |
Build Docker image |
make docker-test |
Build image + run container tests |
make debug |
Clean + Debug build |
make release |
Clean + Release build |
make clean |
Remove build artifacts |
make help |
Show all targets and overrides |
Python bindings
Three packages are published to PyPI:
| Package | PyPI name | Description |
|---|---|---|
| Core DSP | doppler-dsp |
FFT, FIR, NCO, streaming, buffer |
| Spectrum analyzer | doppler-specan |
Live spectrum analyzer web app |
| CLI | doppler-cli |
doppler compose pipeline orchestrator |
pip install doppler-dsp
pip install doppler-specan # spectrum analyzer
pip install doppler-cli # compose / Dopplerfile CLI
From source — build and install the C library first, then install the Python packages:
make && sudo make install && sudo ldconfig
pip install .
pip install python/specan/
pip install python/cli/
The doppler-dsp wheel contains nine compiled extension modules, each
installed inside its own subpackage (doppler.fft, doppler.nco,
doppler.buffer, doppler.stream, doppler.accumulator,
doppler.delay, doppler.resample). The streaming extension
statically links vendored libzmq — no system packages required at
runtime.
CMake directly
cmake -B build -S c -DCMAKE_BUILD_TYPE=Release
cmake --build build --parallel
sudo cmake --install build
CMake options
| Option | Default | Description |
|---|---|---|
USE_FFTW |
ON | FFTW3 backend (default). OFF = pocketfft (MIT-only) |
NumPy_INCLUDE_DIR |
auto | Override NumPy include path (e.g. from a uv venv) |
Python3_EXECUTABLE |
auto | Override Python interpreter (ensures correct ABI suffix) |
FFT backend
To use the bundled pocketfft backend instead of FFTW3:
cmake -DUSE_FFTW=OFF -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build
See README — Licensing for the implications of each choice. License texts: FFTW_LICENSE · POCKETFFT_LICENSE.
Dependencies
C Library Dependencies
All dependencies are available via the standard package manager on each platform.
| Dependency | Version | Ubuntu/Debian | macOS (Homebrew) | Windows (MSYS2) |
|---|---|---|---|---|
| ZeroMQ | ≥ 4.3 | libzmq3-dev |
zeromq |
mingw-w64-x86_64-zeromq |
| FFTW3 | ≥ 3.3 | libfftw3-dev |
fftw |
mingw-w64-x86_64-fftw |
| CMake | ≥ 3.16 | cmake |
cmake |
mingw-w64-x86_64-cmake |
| Python | ≥ 3.12 | python3-dev |
python |
mingw-w64-x86_64-python |
| NumPy | ≥ 2.4 | python3-numpy |
numpy |
mingw-w64-x86_64-python-numpy |
Ubuntu/Debian:
sudo apt-get install libzmq3-dev libfftw3-dev cmake pkg-config python3-dev python3-numpy
macOS (all deps available via Homebrew):
brew install zeromq fftw cmake python numpy
Windows (MSYS2 / UCRT64):
Install MSYS2 and open the UCRT64 shell (not MSYS2, not MinGW64 — the UCRT64 environment uses the modern Universal C Runtime and avoids header-mixing issues).
pacman -S mingw-w64-ucrt-x86_64-gcc \
mingw-w64-ucrt-x86_64-cmake \
mingw-w64-ucrt-x86_64-zeromq \
mingw-w64-ucrt-x86_64-fftw \
mingw-w64-ucrt-x86_64-python \
mingw-w64-ucrt-x86_64-python-numpy \
mingw-w64-ucrt-x86_64-rust \
make pkg-config
Why UCRT64? The MSYS POSIX environment (
/usr/bin/cc) and the UCRT64 native environment (/ucrt64/bin/cc) have incompatible headers. If cmake picks up the wrongccyou'll see errors likeexpected ';' before 'extern'instddef.h. Always launch from the UCRT64 shortcut so/ucrt64/binis first onPATH, and clear any stalebuild/directory before reconfiguring.
Python Extension: Vendored Dependencies
The Python dp_stream extension statically links a vendored copy of libzmq to eliminate runtime dependencies. This means pip install doppler-dsp requires no system packages on the user's machine.
| Dependency | Version | Location | License | Why Vendored |
|---|---|---|---|---|
| libzmq | 4.3.5 | python/vendor/libzmq/ |
MPL-2.0 | Static link in Python extension for zero-dependency installs |
| pocketfft | - | c/src/pocketfft.cc |
BSD-3-Clause | Fallback FFT backend when FFTW not available |
For developers: Vendored libzmq is built automatically when you run make pyext. No manual steps needed.
For users: pip install doppler-dsp includes the vendored libzmq (statically linked). You don't need to install libzmq-dev or any other system packages.
See: VENDORED.md for vendoring policy, update procedures, and licensing details.
Using in your project
From a system install (make install)
CMake (find_package):
find_package(doppler REQUIRED)
target_link_libraries(my_app PRIVATE doppler::doppler)
pkg-config:
gcc -o app main.c $(pkg-config --cflags --libs doppler)
Verify the install is visible to your toolchain:
pkg-config --modversion doppler
Python:
pip install doppler-dsp
Linking directly from the build tree (no install)
Shared library:
gcc -o app main.c \
-Ic/include \
-Lbuild -ldoppler \
$(pkg-config --libs libzmq) \
-Wl,-rpath,$(pwd)/build
Static library (no runtime .so dependency):
gcc -o app main.c \
-Ic/include \
build/libdoppler.a \
$(pkg-config --libs libzmq) -lm
CMake (add_subdirectory):
add_subdirectory(path/to/doppler/c)
target_link_libraries(my_app PRIVATE doppler)
Rust FFI bindings
The ffi/rust/ crate provides idiomatic Rust wrappers around the C library.
It requires no extra Rust dependencies beyond num-complex — all DSP logic
runs in the C library.
Prerequisites
- Rust toolchain — install via the MSYS2 package manager on Windows
(
mingw-w64-ucrt-x86_64-rust) or via rustup on Linux/macOS - The C library built first:
make build
Build and test
make rust-test # build C library + run all 33 Rust tests
make rust-examples # build examples and list their paths
Linux/macOS — build.rs bakes an rpath into every binary so
examples run directly without setting LD_LIBRARY_PATH:
./ffi/rust/target/debug/examples/nco_demo
./ffi/rust/target/debug/examples/fft_demo
./ffi/rust/target/debug/examples/acc_demo
Windows (UCRT64) — the Rust crate links libdoppler.a statically,
so there is no libdoppler.dll runtime dependency. Examples run
directly from the UCRT64 shell:
./ffi/rust/target/debug/examples/nco_demo.exe
./ffi/rust/target/debug/examples/fft_demo.exe
zmq.dll and fftw3.dll are still loaded dynamically; they live in
/ucrt64/bin/ which is on PATH by default in the UCRT64 shell.
The rpath always points at build/c/. After make install, use the
installed system library by passing -DCMAKE_SKIP_RPATH=ON or by
running cargo build with PKG_CONFIG_PATH set to the install prefix.
Modules
| Module | Wraps | Description |
|---|---|---|
acc |
dp_acc_f32_*, dp_acc_cf64_* |
f32 and cf64 accumulators |
fft |
dp_fft_global_setup, dp_fft1d/2d_execute |
1-D and 2-D FFT |
fir |
dp_fir_* |
FIR filter (complex and real taps) |
nco |
dp_nco_* |
Numerically-controlled oscillator |
util::c16_mul |
dp_c16_mul |
SIMD complex multiplication |
Sample types
The crate exposes #[repr(C)] structs that match the C ABI exactly:
| Rust type | C type | Description |
|---|---|---|
DpCf32 |
dp_cf32_t |
Complex f32 (I+Q) — converts to/from Complex<f32> |
DpCi8 |
dp_ci8_t |
Complex i8 |
DpCi16 |
dp_ci16_t |
Complex i16 |
DpCi32 |
dp_ci32_t |
Complex i32 |
Using from another crate
Point Cargo at the local path:
[dependencies]
doppler = { path = "path/to/doppler/ffi/rust" }
Or after publishing to crates.io:
[dependencies]
doppler = "0.1"
Docker
Build and run the library with all dependencies in a container:
# Build image (~130 MB, includes all examples and tests)
docker build -t doppler .
# Run unit tests
docker run --rm doppler /app/test_stream
Expected test output:
========================================
doppler Library Unit Tests
========================================
Test 1: Initialization and cleanup... PASS
Test 2: Sample type utilities... PASS
Test 3: Timestamp generation... PASS
Test 4: Error code strings... PASS
Test 5: Publisher send without subscriber... PASS
Test 6: Publisher-Subscriber communication... PASS (received 5 packets, 500 samples)
Test 7: CI32 data type... PASS
Test 8: Multiple subscribers... PASS
========================================
Results: 8/8 tests passed
========================================
Available binaries in the image
| Binary | Description |
|---|---|
transmitter |
Generates and publishes signal samples over ZMQ PUB |
receiver |
Subscribes and prints signal stats |
spectrum_analyzer |
ASCII real-time spectrum display |
pipeline_demo |
PUSH/PULL pipeline demo (in-process threads) |
fft_demo |
FFT demonstration |
test_stream |
Streaming unit tests |
fft_testbench |
FFT correctness tests |
Docker Compose
Run a complete streaming demo with transmitter, receivers, and spectrum analyzer:
# Start all services (transmitter + 2 receivers + spectrum analyzer)
docker compose up
# Or run just the tests
docker compose --profile test run --rm tests
Example receiver output:
doppler Receiver
===============
Endpoint: tcp://transmitter:5555
Waiting for data... Press Ctrl+C to stop.
First packet received:
Sample Type: CF64
Num Samples: 8192
Sample Rate: 1.00 MHz
Center Freq: 2.40 GHz
Sequence: 0
Power: 0.00 dB
First 5 samples:
[0] I: 1.000000, Q: 0.000000
[1] I: 0.998027, Q: 0.062791
[2] I: 0.992115, Q: 0.125333
[3] I: 0.982287, Q: 0.187381
[4] I: 0.968583, Q: 0.248690
The Dockerfile:
- Multi-stage build (builder + runtime) for minimal image size
- Builds with
-j$(nproc)parallel compilation - Runtime stage includes only
libzmq5andlibfftw3-3 - All example binaries, streaming library, and DSP library are included
STOPSIGNAL SIGTERMfor clean shutdown of signal-handling binaries- Exposes port 5555 for ZMQ streaming