Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion cupyx/signal/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@
from cupyx.signal._acoustics import minimum_phase # NOQA
from cupyx.signal._convolution import convolve1d2o # NOQA
from cupyx.signal._convolution import convolve1d3o # NOQA
from cupyx.signal._radartools import pulse_compression, pulse_doppler, cfar_alpha # NOQA
from cupyx.signal._filtering import channelize_poly # NOQA
from cupyx.signal._filtering import firfilter, firfilter2, firfilter_zi # NOQA
from cupyx.signal._filtering import freq_shift # NOQA
from cupyx.signal._radartools import pulse_compression # NOQA
from cupyx.signal._radartools import pulse_doppler # NOQA
from cupyx.signal._radartools import cfar_alpha # NOQA
from cupyx.signal._radartools import ca_cfar # NOQA
1 change: 1 addition & 0 deletions cupyx/signal/_radartools/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from cupyx.signal._radartools._radartools import pulse_compression # NOQA
from cupyx.signal._radartools._radartools import pulse_doppler # NOQA
from cupyx.signal._radartools._radartools import cfar_alpha # NOQA
from cupyx.signal._radartools._radartools import ca_cfar # NOQA
165 changes: 165 additions & 0 deletions cupyx/signal/_radartools/_radartools.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,3 +142,168 @@ def cfar_alpha(pfa, N):
Alpha value.
"""
return N * (pfa ** (-1.0 / N) - 1)


def ca_cfar(array, guard_cells, reference_cells, pfa=1e-3):
"""
Computes the cell-averaged constant false alarm rate (CA CFAR) detector
threshold and returns for a given array.
Parameters
----------
array : ndarray
Array containing data to be processed.
guard_cells_x : int
One-sided guard cell count in the first dimension.
guard_cells_y : int
One-sided guard cell count in the second dimension.
reference_cells_x : int
one-sided reference cell count in the first dimension.
reference_cells_y : int
one-sided referenc cell count in the second dimension.
pfa : float
Probability of false alarm.
Returns
-------
threshold : ndarray
CFAR threshold
return : ndarray
CFAR detections
"""
shape = array.shape
if len(shape) > 2:
raise TypeError('Only 1D and 2D arrays are currently supported.')
mask = cupy.zeros(shape, dtype=cupy.float32)

if len(shape) == 1:
if len(array) <= 2 * guard_cells + 2 * reference_cells:
raise ValueError('Array too small for given parameters')
intermediate = cupy.cumsum(array, axis=0, dtype=cupy.float32)
N = 2 * reference_cells
alpha = cfar_alpha(pfa, N)
tpb = (32,)
bpg = ((len(array) - 2 * reference_cells - 2 * guard_cells +
tpb[0] - 1) // tpb[0],)
_ca_cfar_1d_kernel(bpg, tpb, (array, intermediate, mask,
len(array), N, cupy.float32(alpha),
guard_cells, reference_cells))
elif len(shape) == 2:
if len(guard_cells) != 2 or len(reference_cells) != 2:
raise TypeError('Guard and reference cells must be two '
'dimensional.')
guard_cells_x, guard_cells_y = guard_cells
reference_cells_x, reference_cells_y = reference_cells
if shape[0] - 2 * guard_cells_x - 2 * reference_cells_x <= 0:
raise ValueError('Array first dimension too small for given '
'parameters.')
if shape[1] - 2 * guard_cells_y - 2 * reference_cells_y <= 0:
raise ValueError('Array second dimension too small for given '
'parameters.')
intermediate = cupy.cumsum(array, axis=0, dtype=cupy.float32)
intermediate = cupy.cumsum(intermediate, axis=1, dtype=cupy.float32)
N = 2 * reference_cells_x * (2 * reference_cells_y +
2 * guard_cells_y + 1)
N += 2 * (2 * guard_cells_x + 1) * reference_cells_y
alpha = cfar_alpha(pfa, N)
tpb = (8, 8)
bpg_x = (shape[0] - 2 * (reference_cells_x + guard_cells_x) + tpb[0] -
1) // tpb[0]
bpg_y = (shape[1] - 2 * (reference_cells_y + guard_cells_y) + tpb[1] -
1) // tpb[1]
bpg = (bpg_x, bpg_y)
_ca_cfar_2d_kernel(bpg, tpb, (array, intermediate, mask,
shape[0], shape[1], N, cupy.float32(alpha),
guard_cells_x, guard_cells_y, reference_cells_x,
reference_cells_y))
return (mask, array - mask > 0)


_ca_cfar_2d_kernel = cupy.RawKernel(r'''
extern "C" __global__ void
_ca_cfar_2d_kernel(float * array, float * intermediate, float * mask,
int width, int height, int N, float alpha,
int guard_cells_x, int guard_cells_y,
int reference_cells_x, int reference_cells_y)
{
int i_init = threadIdx.x+blockIdx.x*blockDim.x;
int j_init = threadIdx.y+blockIdx.y*blockDim.y;
int i, j, x, y, offset;
int tro, tlo, blo, bro, tri, tli, bli, bri;
float outer_area, inner_area, T;
for (i=i_init; i<width-2*(guard_cells_x+reference_cells_x);
i += blockDim.x*gridDim.x){
for (j=j_init; j<height-2*(guard_cells_y+reference_cells_y);
j += blockDim.y*gridDim.y){
/* 'tri' is Top Right Inner (square), 'blo' is Bottom Left
* Outer (square), etc. These are the corners at which
* the intermediate array must be evaluated.
*/
x = i+guard_cells_x+reference_cells_x;
y = j+guard_cells_y+reference_cells_y;
offset = x*height+y;
tro = (x+guard_cells_x+reference_cells_x)*height+y+
guard_cells_y+reference_cells_y;
tlo = (x-guard_cells_x-reference_cells_x-1)*height+y+
guard_cells_y+reference_cells_y;
blo = (x-guard_cells_x-reference_cells_x-1)*height+y-
guard_cells_y-reference_cells_y-1;
bro = (x+guard_cells_x+reference_cells_x)*height+y-
guard_cells_y-reference_cells_y-1;
tri = (x+guard_cells_x)*height+y+guard_cells_y;
tli = (x-guard_cells_x-1)*height+y+guard_cells_y;
bli = (x-guard_cells_x-1)*height+y-guard_cells_y-1;
bri = (x+guard_cells_x)*height+y-guard_cells_y-1;
/* It would be nice to eliminate the triple
* branching here, but it only occurs on the boundaries
* of the array (i==0 or j==0). So it shouldn't hurt
* overall performance much.
*/
if (i>0 && j>0){
outer_area = intermediate[tro]-intermediate[tlo]-
intermediate[bro]+intermediate[blo];
} else if (i == 0 && j > 0){
outer_area = intermediate[tro]-intermediate[bro];
} else if (i > 0 && j == 0){
outer_area = intermediate[tro]-intermediate[tlo];
} else if (i == 0 && j == 0){
outer_area = intermediate[tro];
}
inner_area = intermediate[tri]-intermediate[tli]-
intermediate[bri]+intermediate[bli];
T = outer_area-inner_area;
T = alpha/N*T;
mask[offset] = T;
}
}
}
''', '_ca_cfar_2d_kernel')


_ca_cfar_1d_kernel = cupy.RawKernel(r'''
extern "C" __global__ void
_ca_cfar_1d_kernel(float * array, float * intermediate, float * mask,
int width, int N, float alpha,
int guard_cells, int reference_cells)
{
int i_init = threadIdx.x+blockIdx.x*blockDim.x;
int i, x;
int br, bl, sr, sl;
float big_area, small_area, T;
for (i=i_init; i<width-2*(guard_cells+reference_cells);
i += blockDim.x*gridDim.x){
x = i+guard_cells+reference_cells;
br = x+guard_cells+reference_cells;
bl = x-guard_cells-reference_cells-1;
sr = x+guard_cells;
sl = x-guard_cells-1;
if (i>0){
big_area = intermediate[br]-intermediate[bl];
} else{
big_area = intermediate[br];
}
small_area = intermediate[sr]-intermediate[sl];
T = big_area-small_area;
T = alpha/N*T;
mask[x] = T;
}
}
''', '_ca_cfar_1d_kernel')
3 changes: 3 additions & 0 deletions docs/source/reference/ext.rst
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ through the courtesy of Nvidia cuSignal team.
cupyx.signal.channelize_poly
cupyx.signal.convolve1d3o
cupyx.signal.pulse_compression
cupyx.signal.pulse_doppler
cupyx.signal.cfar_alpha
cupyx.signal.ca_cfar
cupyx.signal.freq_shift

Profiling utilities
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import numpy
import pytest
import scipy

import cupy
from cupy import testing
Expand Down Expand Up @@ -82,3 +83,59 @@ def test_cfar_alpha(dtype):
gpu = signal.cfar_alpha(pfa, N)
cpu = N * (pfa ** (-1.0 / N) - 1)
cupy.testing.assert_allclose(gpu, cpu)


@pytest.mark.parametrize(
"size,gc,rc", [(100, 1, 5), (11, 2, 3), (100, 10, 20)])
@testing.for_float_dtypes(no_float16=True)
@testing.numpy_cupy_allclose(rtol=2e-6, type_check=False)
def test_ca_cfar1d(xp, dtype, size, gc, rc):
array = testing.shaped_random((size,), xp=xp, dtype=dtype)
if xp is cupy:
return signal.ca_cfar(array, gc, rc)
else:
assert xp is numpy
weight = numpy.ones(((rc + gc) * 2 + 1,), dtype=dtype)
weight[rc:-rc] = 0
alpha = numpy.zeros((size,), dtype=dtype)
alpha[gc+rc:-gc-rc] = signal.cfar_alpha(1e-3, 2 * rc)
out = scipy.ndimage.convolve1d(array, weight) * alpha / (2 * rc)
return out, array - out > 0


@pytest.mark.parametrize(
"size,gc,rc", [(1, 1, 1), (10, 2, 3), (10, 0, 5), (10, 5, 0)])
def test_ca_cfar1d_failures(size, gc, rc):
with pytest.raises(ValueError):
_, _ = signal.ca_cfar(cupy.zeros(size), gc, rc)


@pytest.mark.parametrize(
"shape,gc,rc", [((10, 10), (1, 1), (2, 2)), ((10, 100), (1, 10), (2, 20))])
@testing.for_float_dtypes(no_float16=True)
@testing.numpy_cupy_allclose(rtol=2e-6, type_check=False)
def test_ca_cfar2d(xp, dtype, shape, gc, rc):
array = testing.shaped_random(shape, xp=xp, dtype=dtype)
if xp is cupy:
return signal.ca_cfar(array, gc, rc)
else:
assert xp is numpy
rcx, rcy = rc
gcx, gcy = gc
weight = numpy.ones(
((rcx + gcx) * 2 + 1, (rcy + gcy) * 2 + 1), dtype=dtype)
weight[rcx:-rcx, rcy:-rcy] = 0
alpha = numpy.zeros(shape, dtype=dtype)
N = weight.size - (2 * gcx + 1) * (2 * gcy + 1)
alpha[gcx+rcx:-gcx-rcx, gcy+rcy:-gcy-rcy] = signal.cfar_alpha(1e-3, N)
out = scipy.ndimage.convolve(array, weight) * alpha / N
return out, array - out > 0


@pytest.mark.parametrize(
"shape,gc,rc", [((3, 3), (1, 2), (1, 10)),
((3, 3), (1, 1), (10, 1)),
((5, 5), (3, 3), (3, 3))])
def test_ca_cfar2d_failures(shape, gc, rc):
with pytest.raises(ValueError):
_, _ = signal.ca_cfar(cupy.zeros(shape), gc, rc)