Signal processing chain
How Radar turns a data cube into range profiles, range-Doppler maps, angle estimates, detections, point clouds and tracks, and the conventions each stage uses.
witwin.radar.processing turns a simulated cube into the products a radar pipeline uses. Every stage takes a typed record that carries its own axes, so no stage has to guess whether a fast axis holds samples or range bins. This page explains the chain, its records and its conventions.
The chain#
Result.frame(i) -> ProcessingCube [TX, RX, slow, fast]
-> range_profile -> RangeProfile [..., slow, range]
-> range_doppler_map -> RangeDopplerMap [..., doppler, range]
-> combine_coherent -> real map [doppler, range]
-> ca_cfar / os_cfar -> Detections (mask, threshold)
-> point_cloud -> PointCloud (xyz, velocity, energy, range)
-> DetectionFrame -> TrackHandoffSide branches take the same records: music_image and music_spectrum take a range profile, beam_cube takes a range-Doppler map and steering weights, and microdoppler_spectrogram takes a slow-time signal.
Frame: the starting point#
result.frame(i) returns a Frame: one frame's cube [TX, RX, slow, fast] paired with the ProcessingAxes built from the waveform and the array that produced it. Its methods call the processing functions and add no arithmetic:
| Method | Calls |
|---|---|
frame.processing_cube() | Packs the cube and axes as a ProcessingCube |
frame.range_profile(window=None, remove_dc=False) | range_profile |
frame.range_doppler(window="hann", range_window=None, remove_dc=False) | range_profile, then range_doppler_map |
frame.array() | ArrayGeometry.from_axes |
frame.points(...) | range_doppler, combine_coherent, ca_cfar, point_cloud |
Use the functions directly when you need a different detector or estimator.
ProcessingAxes#
ProcessingAxes is the one place a bin index becomes a physical quantity. It holds, in SI units:
range_mandvelocity_mps: float64 axis tensors.range_bin_m,range_origin_m,max_unambiguous_range_m,velocity_bin_mps,max_unambiguous_speed_mps.slow_time_period_s: the slow-time sample period. For TDM-MIMO FMCW it is the slot period,chirp_period · num_tx.wavelength_m,reference_frequency_hz,phasor,doppler_sign.- The array layout:
num_tx,num_rx,element_spacing_m,tx_loc_half_wavelength,rx_loc_half_wavelength.
axes.units maps every field to its unit.
Range#
range_profile dispatches on the waveform stored in the axes:
- FMCW spectrum output: already in range. No second transform runs.
- FMCW beat output: a normalized DFT over the sample axis.
- OFDM: an inverse DFT over subcarriers, which gives the channel impulse response.
- Pulsed: a matched filter against the analytic pulse replica.
window tapers the input of the final transform. The default is rectangular, and a non-rectangular window on an FMCW spectrum cube is refused, because the range transform has already run. Use Fmcw(output="beat") if you want a range window. remove_dc=True subtracts the fast-time mean; it is off by default because it removes clutter.
Doppler#
range_doppler_map transforms the slow axis and keeps range. The Doppler axis is fftshifted and ascending.
Doppler sign
For every waveform, a positive Doppler bin is a closing target (moving toward the radar). The FMCW beat cube and the OFDM and pulsed cubes have opposite phasor conventions, and range_doppler_map is the one place that reconciles them.
The windows are "rectangular", "hann", "hamming" and "blackman". range_doppler_map(window=None) uses rectangular; Frame.range_doppler() uses Hann on the slow axis by default.
Amplitude convention#
Every transform is amplitude-normalized. An isolated path that falls exactly on a bin peaks at its own complex coefficient magnitude times the window's coherent gain (mean(w)), not at a value scaled by the number of samples. RangeProfile.window_coherent_gain and RangeDopplerMap.window_coherent_gain publish that gain. For pulsed waveforms with a window, the identity holds only approximately, because the window tapers the correlation spectrum.
Detection and point clouds#
combine_coherent(rd.data)sums the sensor pairs with their phases (the boresight beam) and returns a real magnitude map. One threshold then means the same thing across the virtual array.ca_cfar(cell-averaging) oros_cfar(ordered-statistic) returnsDetections: a boolean mask and the threshold it beat.ca_cfar_1dworks on a range profile.point_cloud(detections, rd, array, route=...)estimates the angle of each detected cell with"phase_comparison"(default, coarse elevation) or"fft2"(a 2-D FFT over a planar virtual array), compensates the TDM phase, and returns aPointCloud.
Point cloud coordinates are in the array's local frame: x is the array's horizontal axis, y is the component along the boresight, and z is elevation. This differs from the radar pose frame, where the boresight is -z. velocity_mps is closing-positive and energy is in dB.
Angle and imaging#
conventional_steeringandmvdr_weightsbuild beamforming weights. Both satisfyw^H a = 1, so they are interchangeable inbeam_cube.phase_comparison_aoaandfft2_aoareturn direction cosines for a set of cells.music_spectrumandmusic_imagecompute MUSIC pseudo-spectra over a planar array. MUSIC needs a noise subspace, so add receiver noise (for exampleNoise(figure=10.0)) when you use it on simulated data.tdm_compensateremoves the phase a moving target writes across TDM transmitters before angle estimation.
Micro-Doppler and tracking#
microdoppler_spectrogram runs a short-time Fourier transform over a SlowTimeSignal: one sensor pair and one range gate, with uniform timestamps. Frame gaps are not uniform samples, so segment or resample them first. The frequency is the physical Doppler f_D = -f_ref · d(tau)/dt, which is negative for a receding target.
DetectionFrame.from_point_cloud timestamps a point cloud for a tracker, and TrackHandoff sequences frames through an associator. The built-in nearest_neighbour_associator is a reference implementation, not a tracker: it has no track initiation, death or covariance.
Which stages are differentiable#
Range profile, range-Doppler map, beamforming, the matched filter, TDM compensation and MUSIC are differentiable. CFAR, point cloud, angle-of-arrival peak picking and tracking are not: they make discrete decisions, and they refuse inputs that carry a gradient instead of returning a detached answer. See Differentiability.