目录

Using DSP for FFT

Starting from API version 26.0.0, support for Fast Fourier Transform (FFT) and Inverse Fast Fourier Transform (IFFT) features has been introduced.

In Digital Signal Processing (DSP), FFT efficiently converts real-valued time-domain signals into frequency-domain representations, while IFFT restores frequency-domain signals back to the time domain. You can use these APIs to perform spectrum analysis, frequency-domain filtering, or signal reconstruction on audio signals, sensor data, or other time-series data.

Both FFT and IFFT functions offer single-precision (float) and double-precision (double) data type APIs, utilizing efficient algorithms to perform transformations between the time and frequency domains. The maximum supported number of FFT points is 2^16 (that is, 65,536 points). Note: The length of the input signal must be equal to 2^log2n, where log2n is a parameter specified during configuration creation.

Use Cases

FFT and IFFT are applicable to the following typical scenarios:

API Description

For details about the APIs, please refer to the API Reference.

[h2]Configuration Management

API Description
FAST_ErrorCode HMS_FAST_FFT_CreateConfig (FAST_FFTConfig** config, uint32_t log2n) Creates a single-precision FFT configuration object. log2n is the base-2 logarithm of the number of FFT points and must satisfy 0 < log2n <= FAST_MAX_FFT_LOG2N (that is, 1 to 16).
FAST_ErrorCode HMS_FAST_FFT_CreateConfigD (FAST_FFTConfig** config, uint32_t log2n) Creates a double-precision FFT configuration object. log2n is the base-2 logarithm of the number of FFT points and must satisfy 0 < log2n <= FAST_MAX_FFT_LOG2N (that is, 1 to 16).
void HMS_FAST_FFT_DestroyConfig (FAST_FFTConfig* config) Destroys the FFT configuration and releases resources.

[h2]Forward Transform

API Description
FAST_ErrorCode HMS_FAST_FFT_ForwardTransform (FAST_FFTConfig* config, uint32_t length, const float input[], float outputRe[], float outputIm[]) Computes the DFT of a single-precision, real-valued time-domain signal.
FAST_ErrorCode HMS_FAST_FFT_ForwardTransformD (FAST_FFTConfig* config, uint32_t length, const double input[], double outputRe[], double outputIm[]) Computes the DFT of a double-precision, real-valued time-domain signal.

[h2]Inverse Transform

API Description
FAST_ErrorCode HMS_FAST_FFT_InverseTransform (FAST_FFTConfig* config, uint32_t length, const float inputRe[], const float inputIm[], float output[]) Computes the inverse DFT of a single-precision, complex-valued frequency-domain sequence.
FAST_ErrorCode HMS_FAST_FFT_InverseTransformD (FAST_FFTConfig* config, uint32_t length, const double inputRe[], const double inputIm[], double output[]) Computes the inverse DFT of a double-precision, complex-valued frequency-domain sequence.

Development Procedure

This section uses the single-precision API for forward FFT computation as an example. (The single-precision API has no suffix, while the double-precision API has the suffix D.)

  1. Link the related dynamic library in the CMake script.

    find_library(
        lib_fast_dsp
        NAMES fast_dsp
    )
    
    target_link_libraries(entry PRIVATE ${lib_fast_dsp})
  2. Call HMS_FAST_FFT_CreateConfig (single-precision) or HMS_FAST_FFT_CreateConfigD (double-precision) to create an FFT configuration instance (FAST_FFTConfig).

  3. Allocate the input and output arrays based on the FFT size.

    • Forward transform: The input array length is N (real-valued time-domain signal), and the output real and imaginary arrays each have a length of N/2+1.
    • Inverse transform: The input real and imaginary arrays each have a length of N/2+1, and the output array length is N (real-valued time-domain signal).
  4. Call HMS_FAST_FFT_ForwardTransform/HMS_FAST_FFT_ForwardTransformD to compute FFT, or call HMS_FAST_FFT_InverseTransform/HMS_FAST_FFT_InverseTransformD to calculate IFFT.

  5. Call HMS_FAST_FFT_DestroyConfig to destroy the FFT configuration instance and release internal resources.

Sample Code

[h2]Example of single-precision FFT:

#include <cstdio>
#include <cstdlib>
#include <cmath>
#include "FASTKit/fast_dsp_transform.h"

FAST_ErrorCode fft_single_precision_demo() {
    const uint32_t log2n = 4; // FFT size N = 16
    const uint32_t length = 1 << log2n;
    const uint32_t freq_size = length / 2 + 1;

    FAST_FFTConfig *config = nullptr;
    float *input = nullptr;
    float *freqRe = nullptr;
    float *freqIm = nullptr;
    float *output = nullptr;
    FAST_ErrorCode ret = FAST_ERROR_CODE_SUCCESS;

    do {
        // Create a configuration instance.
        ret = HMS_FAST_FFT_CreateConfig(&config, log2n);
        if (ret != FAST_ERROR_CODE_SUCCESS) {
            printf("CreateConfig failed: %d\n", ret);
            break; // If the operation fails, exit and go to the final cleanup area.
        }

        // Allocate memory.
        input = (float *)malloc(length * sizeof(float));
        freqRe = (float *)malloc(freq_size * sizeof(float));
        freqIm = (float *)malloc(freq_size * sizeof(float));
        output = (float *)malloc(length * sizeof(float));

        // Check the allocation result.
        if (!input || !freqRe || !freqIm || !output) {
            printf("Memory allocation failed!\n");
            ret = FAST_ERROR_CODE_FAIL;
            break;
        }

        // Initialize data.
        for (uint32_t i = 0; i < length; ++i) input[i] = (float)i;

        // Forward FFT: time domain -> frequency domain.
        ret = HMS_FAST_FFT_ForwardTransform(config, length, input, freqRe, freqIm);
        if (ret == FAST_ERROR_CODE_SUCCESS) {
            // Print the FFT computation result.
            printf("Frequency domain (first 5 bins):\n");
            for (uint32_t i = 0; i < 5 && i < freq_size; ++i) {
                printf("  Bin %u: (%.4f, %.4fi)\n", i, freqRe[i], freqIm[i]);
            }
        } else {
            printf("ForwardTransform failed: %d\n", ret);
            break;
        }

        // Inverse FFT: frequency domain -> time domain
        ret = HMS_FAST_FFT_InverseTransform(config, length, freqRe, freqIm, output);
        if (ret == FAST_ERROR_CODE_SUCCESS) {
            // Print the IFFT computation result.
            printf("Reconstruction (normalized):\n  Original:  ");
            for (uint32_t i = 0; i < length; ++i) printf("%.4f ", input[i]);
            printf("\n  Recovered: ");
            for (uint32_t i = 0; i < length; ++i) printf("%.4f ", output[i]);
            printf("\n");
        } else {
            printf("InverseTransform failed: %d\n", ret);
            break;
        }

    } while (0);

    if (input) free(input);
    if (freqRe) free(freqRe);
    if (freqIm) free(freqIm);
    if (output) free(output);
    if (config) HMS_FAST_FFT_DestroyConfig(config);

    return ret;
}

[h2]Example of double-precision FFT:

#include <cstdio>
#include <cstdlib>
#include <cmath>
#include "FASTKit/fast_dsp_transform.h"

FAST_ErrorCode fft_double_precision_demo() {
    const uint32_t log2n = 4; // FFT size N = 16
    const uint32_t length = 1 << log2n;
    const uint32_t freq_size = length / 2 + 1;

    FAST_FFTConfig *config = nullptr;
    double *input = nullptr;
    double *freqRe = nullptr;
    double *freqIm = nullptr;
    double *output = nullptr;
    FAST_ErrorCode ret = FAST_ERROR_CODE_SUCCESS;

    do {
        // Create a configuration instance.
        ret = HMS_FAST_FFT_CreateConfigD(&config, log2n);
        if (ret != FAST_ERROR_CODE_SUCCESS) {
            printf("CreateConfig failed: %d\n", ret);
            break; // If the operation fails, exit and go to the final cleanup area.
        }

        // Allocate memory.
        input = (double *)malloc(length * sizeof(double));
        freqRe = (double *)malloc(freq_size * sizeof(double));
        freqIm = (double *)malloc(freq_size * sizeof(double));
        output = (double *)malloc(length * sizeof(double));

        // Check the allocation result.
        if (!input || !freqRe || !freqIm || !output) {
            printf("Memory allocation failed!\n");
            ret = FAST_ERROR_CODE_FAIL;
            break;
        }

        // Initialize data.
        for (uint32_t i = 0; i < length; ++i) input[i] = (double)i;

        // Forward FFT: time domain -> frequency domain.
        ret = HMS_FAST_FFT_ForwardTransformD(config, length, input, freqRe, freqIm);
        if (ret == FAST_ERROR_CODE_SUCCESS) {
            // Print the FFT computation result.
            printf("Frequency domain (first 5 bins):\n");
            for (uint32_t i = 0; i < 5 && i < freq_size; ++i) {
                printf("  Bin %u: (%.4f, %.4fi)\n", i, freqRe[i], freqIm[i]);
            }
        } else {
            printf("ForwardTransform failed: %d\n", ret);
            break;
        }

        // Inverse FFT: frequency domain -> time domain
        ret = HMS_FAST_FFT_InverseTransformD(config, length, freqRe, freqIm, output);
        if (ret == FAST_ERROR_CODE_SUCCESS) {
            // Print the IFFT computation result.
            printf("Reconstruction (normalized):\n  Original:  ");
            for (uint32_t i = 0; i < length; ++i) printf("%.4f ", input[i]);
            printf("\n  Recovered: ");
            for (uint32_t i = 0; i < length; ++i) printf("%.4f ", output[i]);
            printf("\n");
        } else {
            printf("InverseTransform failed: %d\n", ret);
            break;
        }

    } while (0);

    if (input) free(input);
    if (freqRe) free(freqRe);
    if (freqIm) free(freqIm);
    if (output) free(output);
    if (config) HMS_FAST_FFT_DestroyConfig(config);

    return ret;
}

Precautions

  1. FFT size limit: log2n must be between 1 and 16, meaning the FFT size N ranges from 2 to 65536.

  2. Input length matching: The input lengths of the forward and inverse transforms must be the same as the log2n specified during configuration creation (length = 2^log2n).

  3. Output array size: For the FFT with real-valued input, the output array size is length/2+1 because the spectrum of a real-valued signal has conjugate symmetry.

  4. Memory management: After using the FFT configuration, call HMS_FAST_FFT_DestroyConfig to release resources.

  5. Precision selection: The single-precision version features faster computing speed, while the double-precision version features higher precision. Select an appropriate version according to the application scenario.

  6. Thread safety: In a multithreaded environment, multiple threads must not operate on the same FAST_FFTConfig configuration object at the same time. This includes concurrent calls to HMS_FAST_FFT_ForwardTransform (forward transform), HMS_FAST_FFT_InverseTransform (inverse transform), and their corresponding double-precision APIs. To ensure stable computation in a multithreaded environment, use one of the following approaches:

    • Create a separate FAST_FFTConfig object for each thread (recommended, as it is lock-free and provides high performance).
    • Use a mutex to externally protect all calls to the configuration object.