Reception (RF)¶
Reception computes the pulse-echo RF scattered by point targets back onto the receive elements — the raw channel data a scanner would digitise. It is the basis for PSF, phantom, FMC, and sequence (PW/DW) imaging studies, and matches Field II to correlation ~1.0 while running >20× faster on large apertures.
One class, Reception, does everything; its method selector picks how the two-way
SIR is evaluated (speed only — all give the same RF):
"spectral"(default) — the two-way spectrumH_TX·H_RXfrom the closed-form SIR spectrum, evaluated only on the pulse's band; fastest in every measured case."temporal"(="sdi") /"fst"/"auto"— conventional Tupholme-Stepanishen sampled convolution.ReceptionPaired(separate class) — exact but slow pedagogic reference (warns on construction).
Every method supports a global (L,) or per-element (L, E) excitation, a soft baffle
(tx.baffle / rx.baffle = "soft") and attenuation along the round trip transducer
centre → scatterer → receive-element centre.
from esdiva.reception import Reception
sim = Reception(tx, rx, fs=200e6, c=1540) # separate TX / RX transducers
rf, coords = sim.pulse_echo_rf(scatterer_pos_mm, scatterer_amp) # (Erx, Nt)
The physics: the pulse-echo signal is v_pe ⊛ h_tx ⊛ h_rx, with the excitation
and TX/RX impulse responses carrying the band-limited pulse (same convention as
Field II — no explicit derivative applied).
Algorithmic flow¶
flowchart LR
TX[TX transducer] --> R[Reception<br/>fs · c · method]
RX[RX transducer] --> R
R --> M{method}
M -->|spectral · default| SP["closed-form H_TX·H_RX<br/>in-band only · no FFT"]
M -.->|ReceptionPaired · pedagogic| PA[16 corner deltas / pair<br/>splat drive]
M -->|temporal / fst / auto| CV[sample SIRs + FFT]
SP --> API
PA --> API
CV --> API
subgraph API[entry methods]
P1[pulse_echo_rf]
P2[sequence_rf · PW/DW]
P3[synthetic_aperture_rf · FMC]
P4[scan_focusline]
end
API --> RF[rf Erx×Nt, coords]
Entry methods¶
| Method | Purpose | Returns |
|---|---|---|
pulse_echo_rf |
Single transmit; core call. per_scatterer=True → PSF |
(Erx, Nt) / (P, Erx, Nt) |
sequence_rf |
PW/DW event sweep; out_path= checkpoints each event; moving scatterers |
(Nevt, Erx, Nt) |
synthetic_aperture_rf |
FMC — per-element transmit basis | (Etx, Erx, Nt) |
scan_focusline |
One focused B-mode line, RX summed in-kernel | (Nt,) |
The method= flag (spectral / temporal / fst / sdi / auto) only trades
speed — all produce the same RF. ReceptionPaired is a slow pedagogic reference and warns
on construction.
Studies at a glance¶
What you are simulating decides which call you make and which rules matter.
| Study | Call | Page |
|---|---|---|
| PSF — one target's response, resolution and sidelobes | pulse_echo_rf(pts, per_scatterer=True) |
PSF Simulation |
| Phantom — speckle, contrast, B-mode imaging | sequence_rf(pos, amp, events, out_path=…) |
Phantom Simulation |
| Flow — Doppler, decorrelation, moving media | sequence_rf(pos_per_event, amp, events) |
Flow Simulation |
sequence_rf is the recommended entry point for any phantom study
Static or moving, one event or a thousand. The same RF is reachable by
looping pulse_echo_rf by hand — identical physics — but only sequence_rf
carries the on-disk checkpointing that lets a killed run resume, the config
fingerprint that refuses to mix incompatible data, checkpoint_chunks= for
bounding loss inside one event, and t0_per_event gathered for the
beamformer. Reserve pulse_echo_rf for a single acquisition or a PSF map.
Scatterers in, RF out¶
Positions are (N_scat, 3) in mm with (N_scat,) amplitudes; a grid dict (the
same x_extent/dx form emission uses) instead gives a regular lattice of unit
point targets. For tissue, draw them randomly:
from esdiva.utilities import make_phantom
pos, amp = make_phantom(extents_mm, n=20000, echogenicity_map=my_map)
rf, coords = sim.pulse_echo_rf(pos, amp)
A third shape unlocks motion: (N_events, N_scat, 3) gives every emission its
own cloud, which is what makes flow and Doppler possible — see
Flow Simulation.
Lattice ≠ phantom
A periodic grid of scatterers gives coherent echoes (PSF maps), not
speckle. Use make_phantom for speckle statistics.
Preview the setup¶
sim.show(scatterer_positions_mm, amplitudes) renders TX/RX meshes and the
scatterer cloud in 3-D — run it before a long simulation.

Examples¶
| Figure | Study |
|---|---|
![]() |
PSF vs Field II |
![]() |
Full Matrix Capture |
![]() |
Speckle phantom |
Beamforming note¶
coords["t0"] is the beamforming time reference, not the instant of the first
RF sample: it is set so a scatterer's echo peaks at its geometric round-trip time
(|p − r_tx| + |p − r_rx|)/c. Any beamformer — das_volume, das_rca_volume, or
your own — therefore reads the sample at (t_tx + t_rx − t0)·fs with no further
correction.
This is the same contract USTB puts in initial_time and MUST's dasmtx assumes,
so RF exported with save_rf_hdf5 drops into those
workflows unchanged. Getting there costs one shift: the band-limited two-way pulse
peaks about half its length after the geometric arrival, and that lag is subtracted
from t0 at simulation time. It stays in coords["pulse_center_lag_s"] as
provenance — already applied, so adding it again biases the image deep by
c·lag/2 (≈0.5 mm for a 2-cycle 5 MHz pulse model).
t_offset_s on the DAS beamformers defaults to 0.0; use it only for RF from
elsewhere whose time axis is not referenced this way (raw Field II calc_scat
output still carries the lag) or to inject a system delay.
Full signatures: API → Reception · API → Beamforming.


