Skip to content

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 spectrum H_TX·H_RX from 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.

Dual-probe pulse-echo setup

Examples

Figure Study
Concave PSF PSF vs Field II
FMC Full Matrix Capture
Phantom B-mode 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.