RX Capture

Note

Layer 3 shipped, Pass-1. The Layer 3 implementation (emitters, device-fingerprint, tx-impairments, propagation, rx-frontend) landed on branch rfgen-impl-2026-06-25-105955 (PR #94). The class names, Pydantic schemas, and Transformation enum members referenced below match the shipped surface; Pass-1 stubs (GNU Radio OOT emitters, cellular emitters, Sionna propagation backends) construct cleanly and raise an EmitterError or ChannelError tagged with stage="pass1_stub" until backend wiring lands. See Reference / rfgen.emitters and Reference / rfgen.channels for the shipped class roster.

Scientific validation

The RX capture transformations have been scientifically validated against published references. See the per-component reports:

Each report covers construct validity, mathematical correctness against cited equations, empirical comparison to published reference numbers, literature grounding, experimental methodology, operating envelope, and documented limitations.

Receiver capture, abbreviated RX capture, is Group 3 of the four-group channel pipeline. It is where propagated per-emitter components become one receiver-local IQ buffer: select only components inside the receiver’s tuned band, shift each selected component into the receiver frequency frame, filter and resample to the receiver rate, sum the selected components, then inject low-noise amplifier (LNA) thermal noise.

In this page, R_emitter is the emitter component’s native sample rate and R_rx is the receiver’s configured sample rate. Baseband IQ means complex-valued in-phase/quadrature samples whose zero-frequency bin is a reference point rather than the physical carrier itself. In-band means the component’s occupied frequency interval intersects the receiver’s capture interval.

Overview

Name

Transformation

ABC

Default backend

Mix to RX frame

RX_MIXER

BaseRXMixer

Shift each component by emitter.realized_carrier_hz - rx.center_freq_hz; realized_carrier_hz is the concrete carrier frequency drawn for that emitter

IF / bandpass filter

IF_FILTER

BaseIFFilter

Limit the component to rx.bandwidth_hz using a polyphase finite impulse response (FIR) filter

Resample to R_rx

RESAMPLER

BaseResampler

Polyphase rational resampler from R_emitter to R_rx

LNA noise injection

LNA_NOISE

BaseLNANoise

Additive complex Gaussian noise with power kTB * 10^(NF_dB/10); symbols are defined in LNA Noise Injection

The numbering continues from Group 2: T1-T5 are TX impairments, T6 is channel propagation, and these four RX-capture transformations are T7-T10. See Channels overview for the full T1-T14 map.

Note

Sum step. Between BaseResampler and BaseLNANoise, the composer sums the in-band emitter components into one buffer at R_rx. The sum is an operational step rather than a Transformation; it has no plugin slot and cannot be replaced by a user implementation.

The Sum Point

The sum at the head of RX capture, after resampling, is the only place in the pipeline where emitter components are combined. Before this point, each component travels independently through TX impairments and channel propagation. After this point, there is a single composite IQ at each receiver.

The sum is composer-driven: it is not a plugin slot and cannot be replaced by a user implementation. The composer adds all resampled in-band components element-wise at R_rx.

Band-Overlap Predicate

Only emitters whose signal band overlaps the receiver’s capture band contribute to the sum. The default predicate is an interval intersection:

emitter_low_hz  = emitter.realized_carrier_hz - emitter_signal_bw / 2
emitter_high_hz = emitter.realized_carrier_hz + emitter_signal_bw / 2
rx_low_hz       = rx.center_freq_hz - rx.bandwidth_hz / 2
rx_high_hz      = rx.center_freq_hz + rx.bandwidth_hz / 2

overlaps = emitter_low_hz < rx_high_hz and rx_low_hz < emitter_high_hz

When emitter signal bandwidth is not tracked separately, the backend may use this center-frequency approximation:

|emitter.realized_carrier_hz - rx.center_freq_hz| < rx.bandwidth_hz / 2

Emitters that fail this predicate are dropped from that receiver’s component list before the RX mixer. They contribute nothing to that receiver’s IQ or labels, and the current shipped path does not persist a drop-reason audit field in scene metadata.

Sample Rate and Carrier Frame

  • Input to RX mixer: baseband IQ per emitter, at R_emitter, with zero frequency mapped to emitter.realized_carrier_hz.

  • After RX mixer: IQ shifted by emitter.realized_carrier_hz - rx.center_freq_hz, still at R_emitter.

  • After IF filter: bandlimited to rx.bandwidth_hz, still at R_emitter.

  • After resampler: resampled to R_rx.

  • After sum: composite IQ at R_rx, in the receiver’s frequency frame. Direct current (DC), frequency zero in the baseband representation, maps to rx.center_freq_hz.

  • After LNA noise: composite IQ with LNA noise added, at R_rx.

RX hardware receives the output of LNA noise injection and operates entirely at R_rx.

Mix to RX Frame

The RX mixer shifts each in-band component from the emitter’s carrier frequency to the receiver’s frequency frame by applying the complex rotation:

\[ y[n] = x[n] \cdot e^{j 2\pi \Delta f \, n / R_\text{emitter}} \]

where \(\Delta f = f_\text{emitter,carrier} - f_\text{rx,center}\).

Here n is an integer sample index and n / R_emitter is elapsed time in seconds for that component.

A component whose realized_carrier_hz equals rx.center_freq_hz passes through the RX mixer unchanged. A component offset by 1 MHz appears at 1 MHz within the baseband frame after mixing.

Source: GNU Radio’s Frequency Xlating FIR Filter documents the same receiver-side pattern: frequency translation, filtering, and optional decimation in one channel-selection block. The explicit complex-rotation equation is part of rfgen’s proposed BaseRXMixer contract and is cross-linked from Coordinate Systems.

IF/Bandpass Filter

The intermediate-frequency (IF) filter applies a bandpass, or equivalently low-pass after mixing, filter to limit the component to rx.bandwidth_hz. The default implementation uses a linear-phase polyphase FIR filter: linear phase keeps a constant group delay, polyphase is the efficient multi-rate filter structure, and the transition band is chosen so frequencies outside the kept band do not fold back during resampling. Filter order and window type are configurable.

Source: GNU Radio’s Frequency Xlating FIR Filter grounds the paired frequency-translation plus FIR channel-selection model. SciPy’s signal.resample_poly documents the upsample, FIR low-pass, downsample pattern used by the default anti-aliasing path.

Resample to R_rx

The resampler converts from R_emitter to R_rx using a polyphase rational resampler. Each emitter component may have a different R_emitter; the resampler handles each independently before the sum. After resampling, all in-band components share R_rx and can be added element-wise.

Source: SciPy’s signal.resample_poly is the implementation reference for polyphase FIR resampling. Crochiere and Rabiner’s Multirate Digital Signal Processing is the textbook grounding for rational-rate conversion.

LNA Noise Injection

The LNA noise transformation adds thermal noise representing the receiver low-noise amplifier’s contribution. The Johnson-Nyquist formula gives the thermal-noise floor for a resistor over a bandwidth:

\[ P_\text{noise} = k T_0 B \cdot 10^{NF_\text{dB}/10} \]

where \(k\) is Boltzmann’s constant, \(T_0 = 290\,\text{K}\) (IEEE reference temperature), \(B = R_\text{rx}\) for the complex-baseband convention used here, and \(NF_\text{dB}\) is the receiver noise figure. In this convention, the sampled complex IQ buffer spans a two-sided baseband interval whose total bandwidth equals the sample rate.

LNA noise runs after the sum so that one noise realization is injected per receiver, not per component. This correctly models the physical situation: the LNA is at the receiver input and sees the composite RF signal.

Source: Nyquist’s thermal-noise derivation (DOI 10.1103/PhysRev.32.110) grounds the Johnson-Nyquist kTB term, and Friis’ receiver-noise paper (DOI 10.1109/JRPROC.1944.232049) grounds noise figure. IEEE Std 145-2013 gives the \(T_0 = 290\) K reference temperature. The rfgen equation is collected with the Friis cascade model in Reference / RF frontend models.

Per-Receiver Parameters

RX capture runs once per receiver. Each receiver carries its own center_freq_hz, bandwidth_hz, sample_rate_hz, and noise_figure_db when those fields are set. When ReceiverConfig.center_freq_hz, bandwidth_hz, or sample_rate_hz is omitted, the composer falls back to scene_cfg.center_hz, scene_cfg.bandwidth_hz, or scene_cfg.sample_rate_hz before building ChannelRxParams. The scene-level Group.RX_CAPTURE and Group.RX_HARDWARE stages then run once on that receiver’s summed buffer.

Minimal Example

RX capture and RX hardware chain for one receiver. The 5 dB LNA noise figure and 30.72 Msps sample rate are representative example values; exact values come from the receiver model or scenario config.

from rfgen.channel_pipeline import ChannelPipeline
from rfgen.config import ReceiverConfig
from rfgen.rx_frontend import (
    LeesonRXPhaseNoise,
    LinearADCQuantizer,
    LinearAGC,
    LinearLNANoise,
    LinearRXMixer,
    ScipyFIRIFFilter,
    ScipyPolyResampler,
    TorchSigRXIQImbalance,
)

rx_chain = ChannelPipeline([
    # RX capture
    LinearRXMixer(),
    ScipyFIRIFFilter(cutoff_norm=0.4, num_taps=65, gain=1.0),
    ScipyPolyResampler(up=1, down=1),
    # The composer inserts the sum between resampling and LNA noise.
    LinearLNANoise(noise_figure_db=5.0),
    # RX hardware
    LinearADCQuantizer(enob_bits=10),
    LeesonRXPhaseNoise(),
    TorchSigRXIQImbalance(),
    LinearAGC(),
])

rx_cfg = ReceiverConfig(
    rx_id="rx0",
    center_freq_hz=3.5e9,
    bandwidth_hz=20e6,
    sample_rate_hz=30.72e6,
)

The pipeline above is supplied at the scene level; ReceiverConfig carries only the per-receiver RF and geometry parameters. See Channels overview for the full pipeline including the pre-sum chain.

See Also

References

The RX mixer, IF filter, and resampler use frequency-translation, FIR channel-selection, and polyphase-resampling identities; LNA noise is the Johnson-Nyquist thermal-noise floor scaled by receiver noise figure. The math is collected in Reference / RF frontend models and Reference / Noise floor table; the citations below duplicate the inline sources.

  1. GNU Radio. Frequency Xlating FIR Filter. https://wiki.gnuradio.org/index.php/Frequency_Xlating_FIR_Filter (frequency translation plus FIR channel selection)

  2. SciPy. scipy.signal.resample_poly. https://docs.scipy.org/doc/scipy/reference/generated/scipy.signal.resample_poly.html (polyphase FIR upsample, filter, downsample behavior)

  3. Crochiere, R. E. and Rabiner, L. R. Multirate Digital Signal Processing, Prentice Hall, 1983. (polyphase rational resampler)

  4. Vaidyanathan, P. P. Multirate Systems and Filter Banks, Prentice Hall, 1993. (polyphase FIR and filter-bank design)

  5. Friis, H. T. Noise Figures of Radio Receivers, Proc. IRE, 1944. https://doi.org/10.1109/JRPROC.1944.232049 (LNA noise figure, cascade noise factor)

  6. Nyquist, H. Thermal Agitation of Electric Charge in Conductors, Phys. Rev., 1928. https://doi.org/10.1103/PhysRev.32.110 (kT0B Johnson-Nyquist floor)

  7. IEEE Std 145-2013 IEEE Standard for Definitions of Terms for Antennas. https://doi.org/10.1109/IEEESTD.2014.6758443 (reference temperature \(T_0 = 290\) K)