Dopplerfile
A dopplerfile is a small YAML file that registers a custom
pipeline block without writing Python. Drop it next to your script
and doppler compose init picks it up automatically — no registry
edits, no __main__.py changes, no pyproject.toml changes.
Minimal example
# chirp.yml
name: chirp
role: source
executable: ./chirp.py
config:
sample_rate: 2048000.0
sweep_rate: 50000.0
tone_power: -20.0
noise_floor: -90.0
# That's it — just works
doppler compose init chirp specan
doppler compose up
Field reference
| Field | Required | Description |
|---|---|---|
name |
yes | Block name used in doppler compose init |
role |
yes | source, chain, or sink |
executable |
yes | Script or binary to run (see Resolution) |
config |
no | Default parameter values (any scalars, lists, or bools) |
args |
no | Explicit flag mapping (see Arg mapping) |
dependencies |
no | Python packages to install in an isolated env |
Executable resolution
executable follows standard shell resolution:
| Value | Resolved as |
|---|---|
doppler-chirp |
Searched on PATH (e.g. after pip install) |
./chirp.py |
Relative to the working directory |
/home/user/scripts/chirp.py |
Absolute path |
If the executable is not found, Doppler raises a clear error at
compose up time — not silently at scaffold time.
Arg mapping
Auto-map (default)
When args: is omitted, Doppler auto-maps everything by convention:
- source / chain →
--bind {output_addr} - chain / sink →
--connect {input_addr} - Each
configfield →--{field-with-dashes} value - Lists and dicts are JSON-encoded:
--taps '[]' truebecomes a bare flag:--verbosefalseis omitted entirely
Example — sample_rate: 2048000.0 with role: source produces:
./chirp.py --bind tcp://127.0.0.1:5600 --sample-rate 2048000.0 ...
Explicit mapping
For scripts that use non-standard flag names, add an args: section
with {placeholder} substitution:
args:
output: "{output_addr}"
rate: "{sample_rate}"
freq: "{sweep_rate}"
Available placeholders: {output_addr}, {input_addr}, and any
config field name. When args: is present, auto-map is disabled
entirely — you control every flag.
Dependency isolation
Declare Python packages under dependencies: and Doppler wraps the
block with uv run --with at spawn time — an isolated, cached
environment with no global installs required:
name: chirp
role: source
executable: ./chirp.py
dependencies:
- numpy
- scipy
config:
sample_rate: 2048000.0
sweep_rate: 50000.0
uv caches environments by dependency set — first run installs,
subsequent runs are instant. No cleanup needed between chains.
Discovery order
doppler compose init <name> searches for a dopplerfile in this
order:
- Built-in registry —
tone,fir,specan ~/.doppler/blocks/<name>.yml— user's installed blocks./<name>.yml— project-local, next to your script
Place the dopplerfile in your project directory for one-off scripts.
Copy it to ~/.doppler/blocks/ to make it available everywhere.
Full example — FIR-filtered chirp sweep
my-project/
chirp.py # source: sweeping tone via ZMQ PUSH
chirp.yml # dopplerfile
# chirp.yml
name: chirp
role: source
executable: ./chirp.py
dependencies:
- numpy
config:
sample_rate: 2048000.0
sweep_rate: 50000.0
tone_power: -20.0
noise_floor: -90.0
# Scaffold: chirp → FIR → specan
doppler compose init chirp fir specan --name filter-test
# Edit filter-test.yml: set fir.taps to your lowpass coefficients
# Then start:
doppler compose up filter-test
# Watch the chirp paint the filter response via max-hold in the specan
flowchart LR
C["chirp\n(sweeping tone)"]
F["fir\n(lowpass)"]
S["specan\n(max hold)"]
C --> F --> S