File fft_core.h¶
FileList > doppler > fft > fft_core.h
Go to the source code of this file
Per-instance 1-D FFT using pocketfft directly. More...
#include "doppler/clib_common.h"#include "doppler/pocketfft/pocketfft.h"
Classes¶
| Type | Name |
|---|---|
| struct | dp_fft_state_t |
Public Functions¶
| Type | Name |
|---|---|
| dp_fft_state_t * | dp_fft_create (size_t n, int sign, int nthreads) 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. |
| void | dp_fft_destroy (dp_fft_state_t * state) Destroy and free an fft instance. |
| size_t | dp_fft_execute_cf32 (dp_fft_state_t * state, const float _Complex * in, size_t n_in, float _Complex * out, size_t max_out) Compute an out-of-place 1-D DFT on a single-precision complex input. Identical to dp_fft_execute_cf64() but operates on float _Complex (CF32) buffers, halving memory bandwidth relative to the double-precision variant. Output is unnormalised; in andout must not alias. |
| size_t | dp_fft_execute_cf32_max_out (dp_fft_state_t * state) Maximum output samples for CF32 execute (always == n). |
| size_t | dp_fft_execute_cf64 (dp_fft_state_t * state, const double _Complex * in, size_t n_in, double _Complex * out, size_t max_out) 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 andout 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. |
| size_t | dp_fft_execute_cf64_max_out (dp_fft_state_t * state) Maximum output samples per execute call (always == n). |
| size_t | dp_fft_execute_ci16 (dp_fft_state_t * state, const int16_t * in, size_t n_in, float _Complex * out) Compute an out-of-place 1-D DFT directly on integer IQ (ci16). in is interleaved int16 I/Q (2 ints per complex sample, length 2*n); the result is float _Complex (CF32). The int->float scale (v/32768, full-scale ±1.0, matching the cvt module) is folded into the transform's input read, so this is a single fused pass — faster than a separate i16_to_f32 conversion followed bydp_fft_execute_cf32() . Output is unnormalised. |
| size_t | dp_fft_execute_ci16_max_out (dp_fft_state_t * state) Maximum output samples for the ci16 execute (always == n). |
| size_t | dp_fft_execute_ci8 (dp_fft_state_t * state, const int8_t * in, size_t n_in, float _Complex * out) Compute an out-of-place 1-D DFT directly on integer IQ (ci8). As dp_fft_execute_ci16() but in is interleaved int8 I/Q (scale v/128). |
| size_t | dp_fft_execute_ci8_max_out (dp_fft_state_t * state) Maximum output samples for the ci8 execute (always == n). |
| size_t | dp_fft_execute_inplace_cf32 (dp_fft_state_t * state, const float _Complex * in, size_t n_in, float _Complex * out, size_t max_out) Copy in intoout , then transformout in-place (CF32). Single-precision variant ofdp_fft_execute_inplace_cf64() . Copies state->n CF32 samples fromin toout , then transformsout with the CF32 pocketfft plan.in is left unmodified. |
| size_t | dp_fft_execute_inplace_cf32_max_out (dp_fft_state_t * state) Maximum output samples for inplace CF32 (always == n). |
| size_t | dp_fft_execute_inplace_cf64 (dp_fft_state_t * state, const double _Complex * in, size_t n_in, double _Complex * out, size_t max_out) Copy in intoout , then transformout in-place (CF64). The copy step lets callers preserve their input while keeping the output buffer hot in cache. Semantically identical todp_fft_execute_cf64() for separatein /out pointers; use this variant when the caller already ownsout and wants the result there without a second allocation. |
| size_t | dp_fft_execute_inplace_cf64_max_out (dp_fft_state_t * state) Maximum output samples for inplace CF64 (always == n). |
| void | dp_fft_reset (dp_fft_state_t * state) No-op reset (plans are immutable after creation). |
| size_t | dp_fft_smooth_floor (size_t n) The largest 7-smooth length at most n: the longest transform no longer thann whose only prime factors are 2, 3, 5 and 7. |
Detailed Description¶
Holds two pocketfft plans — one for CF64, one for CF32 — allocated at create time for the requested transform length and sign. nthreads is accepted for API compatibility but ignored; pocketfft is single-threaded.
Lifecycle:
dp_fft_state_t *fft = dp_fft_create(1024, -1, 1);
double _Complex out[1024];
dp_fft_execute_cf64(fft, in, 1024, out, 1024);
dp_fft_destroy(fft);
Public Functions Documentation¶
function dp_fft_create¶
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:
nTransform length in samples (power of two recommended).sign-1 for the forward DFT, +1 for the inverse DFT.nthreadsAccepted for API compatibility; ignored.
Returns:
Heap-allocated state, or NULL on allocation failure.
>>> 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)]
function dp_fft_destroy¶
Destroy and free an fft instance.
Parameters:
stateMay be NULL.
function dp_fft_execute_cf32¶
Compute an out-of-place 1-D DFT on a single-precision complex input. Identical to dp_fft_execute_cf64() but operates on float _Complex (CF32) buffers, halving memory bandwidth relative to the double-precision variant. Output is unnormalised;in andout must not alias.
size_t dp_fft_execute_cf32 (
dp_fft_state_t * state,
const float _Complex * in,
size_t n_in,
float _Complex * out,
size_t max_out
)
Parameters:
stateAllocated FFT engine (non-NULL).inInput buffer of length state->n (CF32, row-major).n_inNumber of input samples; must equal state->n; else REFUSED.outOutput buffer of length >= state->n (CF32, caller-allocated).max_outCapacity ofoutin samples. Normally state->n; if it is smaller the first max_out bins are written and the rest of the transform is discarded.
Returns:
min(state->n, max_out) bins. If n_in is not the required length the call is REFUSED with nothing read, written or counted: SIZE_MAX in C, ValueError in Python (#1925).
>>> 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]
function dp_fft_execute_cf32_max_out¶
Maximum output samples for CF32 execute (always == n).
function dp_fft_execute_cf64¶
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 andout 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.
size_t dp_fft_execute_cf64 (
dp_fft_state_t * state,
const double _Complex * in,
size_t n_in,
double _Complex * out,
size_t max_out
)
Parameters:
stateAllocated FFT engine (non-NULL).inInput buffer of length state->n (CF64, row-major).n_inNumber of input samples; must equal state->n; else REFUSED.outOutput buffer of length >= state->n (CF64, caller-allocated).max_outCapacity ofoutin samples. Normally state->n; if it is smaller the first max_out bins are written and the rest of the transform is discarded.
Returns:
min(state->n, max_out) bins. If n_in is not the required length the call is REFUSED with nothing read, written or counted: SIZE_MAX in C, ValueError in Python (#1925).
>>> 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)]
function dp_fft_execute_cf64_max_out¶
Maximum output samples per execute call (always == n).
function dp_fft_execute_ci16¶
Compute an out-of-place 1-D DFT directly on integer IQ (ci16). in is interleaved int16 I/Q (2 ints per complex sample, length 2*n); the result is float _Complex (CF32). The int->float scale (v/32768, full-scale ±1.0, matching the cvt module) is folded into the transform's input read, so this is a single fused pass — faster than a separate i16_to_f32 conversion followed bydp_fft_execute_cf32() . Output is unnormalised.
size_t dp_fft_execute_ci16 (
dp_fft_state_t * state,
const int16_t * in,
size_t n_in,
float _Complex * out
)
Parameters:
stateAllocated FFT engine (non-NULL).inInterleaved int16 I/Q, 2*state->n samples.n_inNumber of complex samples; must equal state->n; else REFUSED.outOutput buffer of length >= state->n (CF32, caller-allocated).
Returns:
n (number of complex samples written). If n_in is not the required length the call is REFUSED with nothing read, written or counted: SIZE_MAX in C, ValueError in Python (#1925).
>>> import numpy as np
>>> from doppler.spectral import FFT
>>> fft = FFT(n=4, sign=-1)
>>> iq = np.full(8, 32768 // 4, dtype=np.int16) # ~0.25 + 0.25j
>>> np.round(fft.execute_ci16(iq).real, 3).tolist()
[1.0, 0.0, 0.0, 0.0]
function dp_fft_execute_ci16_max_out¶
Maximum output samples for the ci16 execute (always == n).
function dp_fft_execute_ci8¶
Compute an out-of-place 1-D DFT directly on integer IQ (ci8). As dp_fft_execute_ci16() butin is interleaved int8 I/Q (scale v/128).
size_t dp_fft_execute_ci8 (
dp_fft_state_t * state,
const int8_t * in,
size_t n_in,
float _Complex * out
)
Parameters:
stateAllocated FFT engine (non-NULL).inInterleaved int8 I/Q, 2*state->n samples.n_inNumber of complex samples; must equal state->n; else REFUSED.outOutput buffer of length >= state->n (CF32, caller-allocated).
Returns:
n (number of complex samples written). If n_in is not the required length the call is REFUSED with nothing read, written or counted: SIZE_MAX in C, ValueError in Python (#1925).
>>> import numpy as np
>>> from doppler.spectral import FFT
>>> fft = FFT(n=4, sign=-1)
>>> iq = np.full(8, 32, dtype=np.int8) # 0.25 + 0.25j
>>> np.round(fft.execute_ci8(iq).real, 3).tolist()
[1.0, 0.0, 0.0, 0.0]
function dp_fft_execute_ci8_max_out¶
Maximum output samples for the ci8 execute (always == n).
function dp_fft_execute_inplace_cf32¶
Copy in intoout , then transformout in-place (CF32). Single-precision variant ofdp_fft_execute_inplace_cf64() . Copies state->n CF32 samples fromin toout , then transformsout with the CF32 pocketfft plan.in is left unmodified.
size_t dp_fft_execute_inplace_cf32 (
dp_fft_state_t * state,
const float _Complex * in,
size_t n_in,
float _Complex * out,
size_t max_out
)
Parameters:
stateAllocated FFT engine (non-NULL).inSource buffer, state->n CF32 samples; not modified.n_inNumber of input samples; must equal state->n; else REFUSED.outDestination buffer, length >= state->n; must not alias in.max_outCapacity ofoutin samples. Normally state->n; if it is smaller the copy-and-transform happens in scratch and only the first max_out bins reachout.
Returns:
min(state->n, max_out) bins. If n_in is not the required length the call is REFUSED with nothing read, written or counted: SIZE_MAX in C, ValueError in Python (#1925).
>>> 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)]
function dp_fft_execute_inplace_cf32_max_out¶
Maximum output samples for inplace CF32 (always == n).
function dp_fft_execute_inplace_cf64¶
Copy in intoout , then transformout in-place (CF64). The copy step lets callers preserve their input while keeping the output buffer hot in cache. Semantically identical todp_fft_execute_cf64() for separatein /out pointers; use this variant when the caller already ownsout and wants the result there without a second allocation.
size_t dp_fft_execute_inplace_cf64 (
dp_fft_state_t * state,
const double _Complex * in,
size_t n_in,
double _Complex * out,
size_t max_out
)
Parameters:
stateAllocated FFT engine (non-NULL).inSource buffer, state->n CF64 samples; not modified.n_inNumber of input samples; must equal state->n; else REFUSED.outDestination buffer, length >= state->n; must not alias in.max_outCapacity ofoutin samples. Normally state->n; if it is smaller the copy-and-transform happens in scratch and only the first max_out bins reachout.
Returns:
min(state->n, max_out) bins. If n_in is not the required length the call is REFUSED with nothing read, written or counted: SIZE_MAX in C, ValueError in Python (#1925).
>>> 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)]
function dp_fft_execute_inplace_cf64_max_out¶
Maximum output samples for inplace CF64 (always == n).
function dp_fft_reset¶
No-op reset (plans are immutable after creation).
function dp_fft_smooth_floor¶
The largest 7-smooth length at most n: the longest transform no longer thann whose only prime factors are 2, 3, 5 and 7.
pocketfft's cost tracks the factorization of the length, not its size. It has its own passes for 2, 3, 4, 5, 7 and 11; a prime factor of 13 or more goes the generic way. Measured at the async-DSSS design point (a slow-time axis of 2D points, one thread, doppler#2457): D = 13 cost 1.22x D = 12, D = 23 1.35x D = 21, and D = 94 (2 x 47) 1.46x D = 90, while a factor of 7 cost 3-5% over its 5-smooth neighbours. A caller free to choose a length at or below a bound a coherent depth, a block size rounds the bound down with this. The round-down gives up at most about 9% of the length (11 -> 10) and less as n grows. A caller whose length is fixed by the signal (a code period) cannot use it.
Parameters:
nUpper bound on the length.
Returns:
The largest m <= n with m = 2^a 3^b 5^c 7^d; 0 when n is 0.
#include <assert.h>
#include "doppler/fft/fft_core.h"
int main (void)
{
assert (dp_fft_smooth_floor (94) == 90); // 2 x 47 -> 2 x 3^2 x 5
assert (dp_fft_smooth_floor (96) == 96); // already smooth
assert (dp_fft_smooth_floor (14) == 14); // 2 x 7 is kept
assert (dp_fft_smooth_floor (13) == 12); // a prime of 13 is not
assert (dp_fft_smooth_floor (407) == 405); // 11 x 37 -> 3^4 x 5
assert (dp_fft_smooth_floor (1) == 1);
return 0;
}
The documentation for this class was generated from the following file native/inc/doppler/fft/fft_core.h