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¶
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)]
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:
execute_cf64_max_out
¶
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:
execute_cf32_max_out
¶
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:
execute_inplace_cf64_max_out
¶
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:
execute_inplace_cf32_max_out
¶
Maximum output samples for inplace CF32 (always == n).
Returns:
| Type | Description |
|---|---|
int
|
Output. |
execute_ci16
¶
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:
execute_ci8
¶
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:
destroy
¶
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 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
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:
execute_cf64_max_out
¶
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:
execute_cf32_max_out
¶
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:
execute_inplace_cf64_max_out
¶
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:
execute_inplace_cf32_max_out
¶
Maximum output samples for inplace CF32 execute (ny * nx).
Returns:
| Type | Description |
|---|---|
int
|
Output. |
destroy
¶
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 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. |
...
|
Related pages¶
Guides — DSSS Burst Acquisition Design — API taxonomy: the DSP building-block hierarchy and its naming axis, DsssReceiver Specifications, DSSS acquisition: stateless, parallel, dynamics-capable, Spectral & Measurement API Map Contributing — DSSS Primary Use Cases for Code Acquisition Design