Model the receiver chain
Add thermal noise, oscillator phase noise, LNA gain, IQ imbalance, AGC, ADC quantization and TX-to-RX leakage to a Radar, and know the order they run in.
A radar with no receiver fields set is an ideal receiver: the cube is the noiseless echo in sqrt(W) at the antenna port. This page shows how to add each receiver stage as a Radar field and what each one does.
The fields#
Radar field | Record | Effect |
|---|---|---|
noise | Noise(...) | Thermal noise from a noise figure, and optional oscillator phase noise |
lna_gain | float, dB | Low-noise amplifier voltage gain |
iq | Iq(gain_db, phase_deg) | Quadrature gain and phase imbalance |
agc | Agc(target_rms, ...) | Automatic gain control to a target RMS |
adc | Adc(bits, full_scale) | Uniform quantization with clipping |
leakage | Leakage(isolation_db, delay_s) | Direct TX-to-RX coupling (FMCW only) |
impedance | float, ohm, default 50.0 | Port impedance for the sqrt(W) to volt conversion |
seed | int, default 0 | Base seed for every random stage |
from witwin.radar import Adc, Agc, Iq, Leakage, Noise
radar = radar.replace(
noise=Noise(figure=10.0),
lna_gain=20.0,
iq=Iq(gain_db=0.5, phase_deg=3.0),
agc=Agc(target_rms=0.1),
adc=Adc(bits=12, full_scale=1.0),
leakage=Leakage(isolation_db=40.0, delay_s=0.2e-9),
seed=7,
)Order#
The stages always run in this order, whatever order you set the fields in:
- Leakage is added during synthesis as one more path row, so the rest of the chain sees it.
- Conversion from sqrt(W) to volts:
v = sqrt(W) · sqrt(impedance). - Thermal noise, phase noise and the LNA gain, in one fused native stage.
- IQ imbalance.
- AGC.
- ADC.
The chain exists only when at least one of noise, lna_gain, agc or adc is set. With a chain, the cube is in volts; without one, it is in sqrt(W).
IQ imbalance alone has no effect
In 0.4.1, a radar with iq set and none of noise, lna_gain, agc or adc builds no receiver chain, so the IQ imbalance is silently skipped and the cube is unchanged. Set another stage as well, for example lna_gain=0.0, to apply it.
For FMCW with the default spectrum output, the chain runs on time-domain beat samples and the result is then transformed to range. You get a range spectrum of a noisy, quantized signal, as a real receiver would produce.
Thermal noise#
Noise(figure=10.0)The noise is circularly symmetric complex Gaussian with total power k · T_sys · B, where:
T_sys = antenna_temperature + 290 · (F - 1)andF = 10^(figure / 10).antenna_temperaturedefaults to 290 K, soT_sys = 290 · F.Bisbandwidthin Hz.None(the default) uses the waveform'sbandwidth: the sample rate for FMCW, the occupied bandnum_subcarriers · subcarrier_spacingfor OFDM, and the declared pulsebandwidthfor pulsed.
Noise() with the default figure=0.0 still adds thermal noise at 290 K. noise.noise_power_watts and noise.thermal_sigma_volts(impedance) give the resulting levels.
Oscillator phase noise#
Noise(figure=10.0, phase_density=-90.0, phase_offset=1e6)phase_density is the single-sideband phase-noise density in dBc/Hz measured at phase_offset Hz. The model is a free-running (Wiener) oscillator with a -20 dB/decade slope. It does not model the close-in 1/f³ region or a far-out floor.
In a scene simulation, one oscillator is evaluated at the receive time and at the delayed transmit time of every path, so phase noise correlates across paths, antennas and frames as it would in hardware. This needs ADC-time observations, so:
- Phase noise works with FMCW only.
Motion.auto()resolves to per-ADC sampling when phase noise is set.Motion.static()andMotion.chirp()are refused with phase noise.
LNA, AGC and ADC#
lna_gainis a voltage gain in dB. The noise is referred to the receiver input, so the LNA scales signal and noise together.Agc(target_rms, mode="per_rx", min_gain=-60.0, max_gain=60.0)scales the signal towardtarget_rmsvolts.mode="per_rx"measures each receiver separately and"global"uses one gain. The gain is limited to[min_gain, max_gain]dB. AGC makes the receiver nonlinear in the signal.Adc(bits, full_scale)quantizes each component uniformly (mid-tread) with step2 · full_scale / (2^bits - 1)and clips values beyond±full_scalevolts.bitsis 1 to 30.
IQ imbalance#
Iq(gain_db, phase_deg) models a demodulator whose two branches differ by a gain ratio and a phase error. It leaks a conjugate image: y = alpha · x + beta · conj(x). On a range-Doppler map the image appears at the mirrored Doppler bin, iq.image_rejection_db below the true return. Use it to train or test a classifier that must not rely on a clean Doppler sign.
Leakage#
Leakage(isolation_db, delay_s) adds the part of the transmitted chirp that reaches the receiver directly. After de-chirping it is a strong tone at a range of centimetres. Its amplitude is sqrt(P_tx) · 10^(-isolation_db / 20) and its delay is delay_s. It spends the receiver's dynamic range and puts window sidelobes over the near range bins. Leakage is FMCW only: an OFDM or pulsed radar with leakage raises NotImplementedError at synthesis.
Seeds and repeatability#
All random stages derive their own streams from seed with a counter-based generator. The same seed gives a bitwise identical cube, and turning one stage on does not change another stage's random draws. Use radar.replace(seed=s) to draw a new noise realization.
Gradients through the chain#
Noise, LNA and AGC are differentiable with respect to the signal. ADC quantization refuses a differentiable input, and every receiver setting (noise figure, gain, AGC limits, ADC bits) must be a host float, not a tensor. See Differentiability.