Skip to content

Python FFT API

1-D and 2-D FFT backed by the vendored pocketfft (pure C99, libm-only). Each FFT / FFT2D instance owns an independent plan — no global state, thread-safe, multiple sizes coexist freely. CF64 transforms run natively; CF32 transforms are computed in double precision and returned as complex64.

Source: src/doppler/spectral/__init__.py


Dtype dispatch

Pass any dtype — the right C path is chosen automatically:

Input dtype C path Speed
complex64 CF32 → computed in double slower (float↔double conversion)
complex128 CF64 (native double) baseline

Examples

1-D FFT

from doppler.spectral import FFT
import numpy as np

f = FFT(1024)

# CF32 — single precision (~2× faster)
x32 = (np.random.randn(1024) + 1j * np.random.randn(1024)).astype(np.complex64)
X32 = f.execute_cf32(x32)
assert X32.dtype == np.complex64

# CF64 — double precision
x64 = np.random.randn(1024) + 1j * np.random.randn(1024)
X64 = f.execute_cf64(x64)
assert X64.dtype == np.complex128

# In-place
f.execute_inplace_cf32(x32)

Inverse FFT

fwd = FFT(1024, sign=-1)   # forward (default)
inv = FFT(1024, sign=+1)   # inverse

X = fwd.execute_cf32(x32)      # x32 from the block above
x_back = inv.execute_cf32(X)   # round-trip (unnormalised — divide by N)

2-D FFT

from doppler.spectral import FFT2D
import numpy as np

f2 = FFT2D(64, 64)
x = (np.random.randn(64, 64) + 1j * np.random.randn(64, 64)).astype(np.complex64)
X = f2.execute_cf32(x)
f2.execute_inplace_cf32(x)

Repeated transforms

f = FFT(1024)
iq_stream = [x32, x32]             # a couple of complex64 blocks
for block in iq_stream:            # generator of complex64 arrays
    X = f.execute_cf32(block)      # plan reused; no re-allocation
    power = np.abs(X) ** 2

Multiple sizes in flight

small = FFT(256)
large = FFT(4096)
# Independent plans — coexist with no conflict

FFT

Allocate a reusable 1-D FFT engine for a fixed length and sign. Two pocketfft plans are created at construction time — one for CF64 and one for CF32 — so execute calls carry no plan-setup overhead. The same instance may be called repeatedly for independent input vectors of the same length. nthreads is accepted for API parity but is ignored; pocketfft plans are single-threaded.

Parameters:

Name Type Description Default
n int

Transform length in samples (power of two recommended).

1024
sign int

-1 for the forward DFT, +1 for the inverse DFT.

-1
nthreads int

Accepted for API compatibility; ignored.

1

Examples:

>>> from doppler.spectral import FFT
>>> import numpy as np
>>> fft = FFT(n=4, sign=-1, nthreads=1)
>>> fft.n, fft.sign
(4, -1)
>>> x = np.array([1, 0, 0, 0], dtype=np.complex64)
>>> fft.execute_cf32(x).tolist()
[(1+0j), (1+0j), (1+0j), (1+0j)]

n property

n: int

Transform length (samples).

sign property

sign: int

-1 forward, +1 inverse.

reset

reset() -> None

No-op reset (plans are immutable after creation).

execute_cf64

execute_cf64(
    x: NDArray[complex128],
    out: NDArray[complex128] | None = None,
) -> NDArray[np.complex128]

Compute an out-of-place 1-D DFT on a double-precision complex input. The output is written to a fresh caller-supplied buffer; in and out must not alias. The transform is unnormalised: the inverse DFT (sign=+1) does NOT divide by n. Both buffers must be exactly state->n elements long.

Parameters:

Name Type Description Default
x NDArray[complex128]

Input.

required

Returns:

Type Description
NDArray[complex128]

min(state->n, max_out) bins.

Examples:

>>> from doppler.spectral import FFT
>>> import numpy as np
>>> fft = FFT(n=4, sign=-1)
>>> x = np.array([1, 0, 0, 0], dtype=np.complex128)
>>> fft.execute_cf64(x).tolist()
[(1+0j), (1+0j), (1+0j), (1+0j)]

execute_cf64_max_out

execute_cf64_max_out() -> int

Maximum output samples per execute call (always == n).

Returns:

Type Description
int

Output.

execute_cf32

execute_cf32(
    x: NDArray[complex64],
    out: NDArray[complex64] | None = None,
) -> NDArray[np.complex64]

Compute an out-of-place 1-D DFT on a single-precision complex input. Identical to fft_execute_cf64() but operates on float complex (CF32) buffers, halving memory bandwidth relative to the double-precision variant. Output is unnormalised; in and out must not alias.

Parameters:

Name Type Description Default
x NDArray[complex64]

Input.

required

Returns:

Type Description
NDArray[complex64]

min(state->n, max_out) bins.

Examples:

>>> from doppler.spectral import FFT
>>> import numpy as np
>>> fft = FFT(n=4, sign=-1)
>>> x = np.ones(4, dtype=np.complex64)
>>> fft.execute_cf32(x).tolist()
[(4+0j), 0j, 0j, 0j]

execute_cf32_max_out

execute_cf32_max_out() -> int

Maximum output samples for CF32 execute (always == n).

Returns:

Type Description
int

Output.

execute_inplace_cf64

execute_inplace_cf64(
    x: NDArray[complex128],
    out: NDArray[complex128] | None = None,
) -> NDArray[np.complex128]

Copy in into out, then transform out in-place (CF64). The copy step lets callers preserve their input while keeping the output buffer hot in cache. Semantically identical to fft_execute_cf64() for separate in / out pointers; use this variant when the caller already owns out and wants the result there without a second allocation.

Parameters:

Name Type Description Default
x NDArray[complex128]

Input.

required

Returns:

Type Description
NDArray[complex128]

min(state->n, max_out) bins.

Examples:

>>> from doppler.spectral import FFT
>>> import numpy as np
>>> fft = FFT(n=4, sign=-1)
>>> x = np.array([1, 0, 0, 0], dtype=np.complex128)
>>> fft.execute_inplace_cf64(x).tolist()
[(1+0j), (1+0j), (1+0j), (1+0j)]

execute_inplace_cf64_max_out

execute_inplace_cf64_max_out() -> int

Maximum output samples for inplace CF64 (always == n).

Returns:

Type Description
int

Output.

execute_inplace_cf32

execute_inplace_cf32(
    x: NDArray[complex64],
    out: NDArray[complex64] | None = None,
) -> NDArray[np.complex64]

Copy in into out, then transform out in-place (CF32). Single-precision variant of fft_execute_inplace_cf64(). Copies state->n CF32 samples from in to out, then transforms out with the CF32 pocketfft plan. in is left unmodified.

Parameters:

Name Type Description Default
x NDArray[complex64]

Input.

required

Returns:

Type Description
NDArray[complex64]

min(state->n, max_out) bins.

Examples:

>>> from doppler.spectral import FFT
>>> import numpy as np
>>> fft = FFT(n=4, sign=-1)
>>> x = np.array([1, 0, 0, 0], dtype=np.complex64)
>>> fft.execute_inplace_cf32(x).tolist()
[(1+0j), (1+0j), (1+0j), (1+0j)]

execute_inplace_cf32_max_out

execute_inplace_cf32_max_out() -> int

Maximum output samples for inplace CF32 (always == n).

Returns:

Type Description
int

Output.

execute_ci16

execute_ci16(iq: NDArray[int16]) -> NDArray[np.complex64]

Out-of-place 1-D FFT directly on interleaved int16 I/Q (CF32 out). The int16->float convert (v/32768, full-scale +/-1.0) is fused into the transform, so it is faster than i16_to_f32 then execute_cf32.

Examples:

>>> import numpy as np
>>> from doppler.spectral import FFT
>>> obj = FFT(1024, -1, 1)
>>> y = obj.execute_ci16(np.zeros(2048, dtype=np.int16))
>>> y.dtype
dtype('complex64')

execute_ci8

execute_ci8(iq: NDArray[int8]) -> NDArray[np.complex64]

Out-of-place 1-D FFT directly on interleaved int8 I/Q (CF32 out). As execute_ci16 but int8 input (v/128, full-scale +/-1.0).

Examples:

>>> import numpy as np
>>> from doppler.spectral import FFT
>>> obj = FFT(1024, -1, 1)
>>> y = obj.execute_ci8(np.zeros(2048, dtype=np.int8))
>>> y.dtype
dtype('complex64')

destroy

destroy() -> None

Release the underlying C resources immediately.

Ordinarily unnecessary: the resources are freed when the object is garbage-collected. Call this to release them at a definite point instead, or use the object as a context manager, which calls it on exit.

Idempotent: calling it again on an already-released object does nothing. Every other method raises RuntimeError once it has run.

__enter__

__enter__() -> FFT

Enter a context manager, returning this object.

Lets a FFT be used in a with statement so its C resources are released deterministically on exit rather than at collection time.

Returns:

Type Description
FFT

This same object, not a copy.

__exit__

__exit__(
    exc_type: object | None = ...,
    exc: object | None = ...,
    tb: object | None = ...,
) -> None

Exit a context manager, releasing the FFT.

Equivalent to calling destroy(). Returns None, so an exception raised inside the with body propagates normally; this never suppresses one.

Parameters:

Name Type Description Default
exc_type object | None

Exception class, or None. Ignored.

...
exc object | None

Exception instance, or None. Ignored.

...
tb object | None

Traceback object, or None. Ignored.

...

FFT2D

Allocate a reusable 2-D FFT engine for a fixed ny×nx grid. Two pocketfft 2-D plans are built at construction time — one CF64, one CF32. All execute calls accept and return flat row-major arrays of length ny*nx; the Python layer may reshape them with .reshape(ny, nx). nthreads is accepted for API parity but ignored.

Parameters:

Name Type Description Default
ny int

Number of rows (outer dimension).

64
nx int

Number of columns (inner dimension).

64
sign int

-1 for the forward DFT, +1 for the inverse DFT.

-1
nthreads int

Accepted for API compatibility; ignored.

1

Examples:

>>> from doppler.spectral import FFT2D
>>> import numpy as np
>>> fft2d = FFT2D(ny=4, nx=4, sign=-1, nthreads=1)
>>> fft2d.ny, fft2d.nx, fft2d.sign
(4, 4, -1)
>>> x = np.zeros(16, dtype=np.complex64); x[0] = 1.0
>>> out = fft2d.execute_cf32(x)
>>> out.shape, out.dtype
((16,), dtype('complex64'))
>>> bool(np.allclose(out, 1.0))
True

ny property

ny: int

Row count.

nx property

nx: int

Column count.

sign property

sign: int

-1 forward, +1 inverse.

reset

reset() -> None

No-op reset (plans are immutable after creation).

execute_cf64

execute_cf64(
    x: NDArray[complex128],
    out: NDArray[complex128] | None = None,
) -> NDArray[np.complex128]

Compute an out-of-place 2-D DFT on a double-precision complex grid. in is a flat row-major CF64 array of length nynx. The output is written to the caller-supplied out buffer (also nynx); the two must not alias. The transform is unnormalised.

Parameters:

Name Type Description Default
x NDArray[complex128]

Input.

required

Returns:

Type Description
NDArray[complex128]

min(ny*nx, max_out) samples.

Examples:

>>> from doppler.spectral import FFT2D
>>> import numpy as np
>>> fft2d = FFT2D(ny=4, nx=4, sign=-1)
>>> x = np.zeros(16, dtype=np.complex128); x[0] = 1.0
>>> out = fft2d.execute_cf64(x)
>>> out.shape, out.dtype
((16,), dtype('complex128'))
>>> bool(np.allclose(out, 1.0))
True

execute_cf64_max_out

execute_cf64_max_out() -> int

Maximum output samples per execute call (ny * nx).

Returns:

Type Description
int

Output.

execute_cf32

execute_cf32(
    x: NDArray[complex64],
    out: NDArray[complex64] | None = None,
) -> NDArray[np.complex64]

Compute an out-of-place 2-D DFT on a single-precision complex grid. Single-precision variant of fft2d_execute_cf64(). Accepts and returns flat row-major CF32 arrays of length ny*nx. Output is unnormalised; in and out must not alias.

Parameters:

Name Type Description Default
x NDArray[complex64]

Input.

required

Returns:

Type Description
NDArray[complex64]

min(ny*nx, max_out) samples.

Examples:

>>> from doppler.spectral import FFT2D
>>> import numpy as np
>>> fft2d = FFT2D(ny=4, nx=4, sign=-1)
>>> x = np.zeros(16, dtype=np.complex64); x[0] = 1.0
>>> out = fft2d.execute_cf32(x)
>>> out.shape, out.dtype
((16,), dtype('complex64'))
>>> bool(np.allclose(out, 1.0))
True

execute_cf32_max_out

execute_cf32_max_out() -> int

Maximum output samples for CF32 execute (ny * nx).

Returns:

Type Description
int

Output.

execute_inplace_cf64

execute_inplace_cf64(
    x: NDArray[complex128],
    out: NDArray[complex128] | None = None,
) -> NDArray[np.complex128]

Copy in into out, then transform out in-place (CF64 2-D). The ny*nx CF64 samples from in are first memcpy'd to out; the 2-D DFT is then applied to out in-place. in is left unmodified. Useful when the caller owns out and wants to preserve in.

Parameters:

Name Type Description Default
x NDArray[complex128]

Input.

required

Returns:

Type Description
NDArray[complex128]

min(ny*nx, max_out) samples.

Examples:

>>> from doppler.spectral import FFT2D
>>> import numpy as np
>>> fft2d = FFT2D(ny=4, nx=4, sign=-1)
>>> x = np.zeros(16, dtype=np.complex128); x[0] = 1.0
>>> out = fft2d.execute_inplace_cf64(x)
>>> bool(np.allclose(out, 1.0))
True

execute_inplace_cf64_max_out

execute_inplace_cf64_max_out() -> int

Maximum output samples for inplace CF64 execute (ny * nx).

Returns:

Type Description
int

Output.

execute_inplace_cf32

execute_inplace_cf32(
    x: NDArray[complex64],
    out: NDArray[complex64] | None = None,
) -> NDArray[np.complex64]

Copy in into out, then transform out in-place (CF32 2-D). Single-precision variant of fft2d_execute_inplace_cf64(). Copies ny*nx CF32 samples then applies the CF32 2-D pocketfft plan to out.

Parameters:

Name Type Description Default
x NDArray[complex64]

Input.

required

Returns:

Type Description
NDArray[complex64]

min(ny*nx, max_out) samples.

Examples:

>>> from doppler.spectral import FFT2D
>>> import numpy as np
>>> fft2d = FFT2D(ny=4, nx=4, sign=-1)
>>> x = np.zeros(16, dtype=np.complex64); x[0] = 1.0
>>> out = fft2d.execute_inplace_cf32(x)
>>> bool(np.allclose(out, 1.0))
True

execute_inplace_cf32_max_out

execute_inplace_cf32_max_out() -> int

Maximum output samples for inplace CF32 execute (ny * nx).

Returns:

Type Description
int

Output.

destroy

destroy() -> None

Release the underlying C resources immediately.

Ordinarily unnecessary: the resources are freed when the object is garbage-collected. Call this to release them at a definite point instead, or use the object as a context manager, which calls it on exit.

Idempotent: calling it again on an already-released object does nothing. Every other method raises RuntimeError once it has run.

__enter__

__enter__() -> FFT2D

Enter a context manager, returning this object.

Lets a FFT2D be used in a with statement so its C resources are released deterministically on exit rather than at collection time.

Returns:

Type Description
FFT2D

This same object, not a copy.

__exit__

__exit__(
    exc_type: object | None = ...,
    exc: object | None = ...,
    tb: object | None = ...,
) -> None

Exit a context manager, releasing the FFT2D.

Equivalent to calling destroy(). Returns None, so an exception raised inside the with body propagates normally; this never suppresses one.

Parameters:

Name Type Description Default
exc_type object | None

Exception class, or None. Ignored.

...
exc object | None

Exception instance, or None. Ignored.

...
tb object | None

Traceback object, or None. Ignored.

...

GuidesDSSS Burst Acquisition DesignAPI taxonomy: the DSP building-block hierarchy and its naming axis, DsssReceiver Specifications, DSSS acquisition: stateless, parallel, dynamics-capable, Spectral & Measurement API Map ContributingDSSS Primary Use Cases for Code Acquisition Design