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_cloudDetectors 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 property | Meaning |
|---|---|
mask | Boolean tensor, True where a cell beat its threshold. |
threshold | The threshold of every cell, same shape. |
count | Number 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) -> DetectionsCell-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.
Real magnitude map [..., D, R], for example from combine_coherent.
(2, 3)Guard cells on each side, (doppler, range).
(4, 6)Training cells on each side beyond the guard cells, (doppler, range).
0.001Design 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) -> DetectionsOrdered-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.
Real magnitude map [..., D, R].
(2, 3)Guard cells on each side, (doppler, range).
(4, 6)Training cells on each side, (doppler, range).
0.75Which ordered training sample sets the noise level, as a fraction of the training count.
0.001Design 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) -> DetectionsCell-averaging CFAR along range only, over [..., R], with the same replicate-padded ring.
Real magnitude profile [..., R].
2Guard cells on each side.
8Training cells on each side beyond the guard cells.
0.001Design 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) -> PointCloudTurn 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.
Detections with a 2-D [D, R] mask over the coherently combined map.
The full [TX, RX, D, R] map. Its axes give range and velocity.
The virtual array, from frame.array().
'phase_comparison'Angle estimator: "phase_comparison" or "fft2". See Angle estimation.
64Angle FFT size.
NoneKeep only cells with lo <= range < hi, in metres. None keeps every range.
NoneKeep only the strongest cells. None keeps every detection.
FalseKeep only closing targets.
1e-06Floor 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 method | Meaning |
|---|---|
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. |
device | Device 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()