Detection and point clouds

CA-CFAR, OS-CFAR and 1-D CFAR detectors, the Detections record, and point_cloud with the PointCloud record in witwin.radar.processing.

This page covers detection and point clouds in witwin.radar.processing. Frame.points() runs combine_coherent, ca_cfar and point_cloud for you. See Detect targets and build point clouds.

from witwin.radar.processing import POINT_CLOUD_COLUMNS, Detections, PointCloud, ca_cfar, ca_cfar_1d, os_cfar, point_cloud

Detectors and point_cloud are not differentiable and refuse inputs that carry a gradient.

Detections#

class Detections(mask, threshold)

A boolean detection mask and the adaptive threshold each cell was compared against. Both have the shape of the input map, batch dimensions included.

Field or propertyMeaning
maskBoolean tensor, True where a cell beat its threshold.
thresholdThe threshold of every cell, same shape.
countNumber of detections as a 0-dim device tensor. Reading it as a Python number synchronizes the GPU.

ca_cfar#

ca_cfar(rd_map, *, guard_cells=(2, 3), training_cells=(4, 6), pfa=0.001) -> Detections

Cell-averaging CFAR over [..., D, R]. The noise estimate is the mean of the training ring around each cell. Edges use replicate padding, so a border cell sees a ring of the same size.

rd_maptorch.Tensorrequired

Real magnitude map [..., D, R], for example from combine_coherent.

guard_cellstuple[int, int]default (2, 3)

Guard cells on each side, (doppler, range).

training_cellstuple[int, int]default (4, 6)

Training cells on each side beyond the guard cells, (doppler, range).

pfafloatdefault 0.001

Design false-alarm probability.

rd = frame.range_doppler()
detections = ca_cfar(combine_coherent(rd.data), pfa=1e-4)
print(int(detections.count))

os_cfar#

os_cfar(rd_map, *, guard_cells=(2, 3), training_cells=(4, 6), rank_fraction=0.75, pfa=0.001) -> Detections

Ordered-statistic CFAR over [..., D, R]. The noise estimate is the rank_fraction quantile of the training cells instead of their mean, which keeps a second target in the ring from raising the threshold. It stores every cell's training patch, so it uses more memory than ca_cfar.

rd_maptorch.Tensorrequired

Real magnitude map [..., D, R].

guard_cellstuple[int, int]default (2, 3)

Guard cells on each side, (doppler, range).

training_cellstuple[int, int]default (4, 6)

Training cells on each side, (doppler, range).

rank_fractionfloatdefault 0.75

Which ordered training sample sets the noise level, as a fraction of the training count.

pfafloatdefault 0.001

Design constant in the cell-averaging form. The achieved false-alarm rate is lower: at the defaults with 40 training cells it measured about 0.1 of the declared value for pfa=1e-3.

ca_cfar_1d#

ca_cfar_1d(profile, *, guard_cells=2, training_cells=8, pfa=0.001) -> Detections

Cell-averaging CFAR along range only, over [..., R], with the same replicate-padded ring.

profiletorch.Tensorrequired

Real magnitude profile [..., R].

guard_cellsintdefault 2

Guard cells on each side.

training_cellsintdefault 8

Training cells on each side beyond the guard cells.

pfafloatdefault 0.001

Design false-alarm probability.

point_cloud#

point_cloud(detections, rd, array, *, route='phase_comparison', fft_size=64, range_gate_m=None,
            max_points=None, positive_velocity_only=False, energy_floor=1e-06) -> PointCloud

Turn detected range-Doppler cells into points. For each cell it reads the velocity and range from the map's axes, applies TDM phase compensation, and estimates the angle with the chosen route.

detectionsDetectionsrequired

Detections with a 2-D [D, R] mask over the coherently combined map.

rdRangeDopplerMaprequired

The full [TX, RX, D, R] map. Its axes give range and velocity.

arrayArrayGeometryrequired

The virtual array, from frame.array().

routestrdefault 'phase_comparison'

Angle estimator: "phase_comparison" or "fft2". See Angle estimation.

fft_sizeintdefault 64

Angle FFT size.

range_gate_mtuple[float, float] | Nonedefault None

Keep only cells with lo <= range < hi, in metres. None keeps every range.

max_pointsint | Nonedefault None

Keep only the strongest cells. None keeps every detection.

positive_velocity_onlybooldefault False

Keep only closing targets.

energy_floorfloatdefault 1e-06

Floor added before the cell energy is converted to dB.

Returns PointCloud, float64, on the input device.

cloud = point_cloud(detections, rd, frame.array(), range_gate_m=(0.5, 6.0), max_points=128)

Raises ValueError for route="fft2" on an array with fewer than four transmitters.

PointCloud#

class PointCloud(xyz, velocity_mps, energy, range_m)

N detections in the array frame used by the point-cloud stage: x along the array's horizontal axis, y along the boresight, z in elevation, in metres.

Field or methodMeaning
xyz[N, 3] positions, m.
velocity_mps[N] radial velocity, m/s, closing positive.
energy[N] cell energy, dB relative to one unit of the map amplitude.
range_m[N] range, m.
len(cloud)N.
deviceDevice of the tensors.
as_columns()[N, 6] tensor in POINT_CLOUD_COLUMNS order.
select(keep)A new PointCloud with the rows where the boolean keep is True.
PointCloud.empty(*, device)An empty cloud.

POINT_CLOUD_COLUMNS is ('x', 'y', 'z', 'velocity_mps', 'energy', 'range_m').

near = cloud.select(cloud.range_m < 4.0)
columns = near.as_columns()