/**
* @file tiny_fft_test.c
* @author SHUAIWEN CUI (SHUAIWEN001@e.ntu.edu.sg)
* @brief tiny_fft | test | source
* @version 1.0
* @date 2025-11-16
* @copyright Copyright (c) 2025
*
*/
/* DEPENDENCIES */
#include "tiny_fft_test.h"
#include <math.h>
#include <stdio.h>
#include <string.h>
/**
* @brief Generate a test signal with known frequency components
*/
static void generate_test_signal(float *signal, int len, float sample_rate)
{
// Generate signal: 0.9*sin(2*pi*15*t) + 0.4*cos(2*pi*70*t) + 0.2*sin(2*pi*120*t)
// Frequencies: 15 Hz, 70 Hz, and 120 Hz
for (int i = 0; i < len; i++)
{
float t = (float)i / sample_rate;
signal[i] = 0.9f * sinf(2.0f * M_PI * 15.0f * t) +
0.4f * cosf(2.0f * M_PI * 70.0f * t) +
0.2f * sinf(2.0f * M_PI * 120.0f * t);
}
}
void tiny_fft_test(void)
{
printf("========== TinyFFT Test ==========\n\n");
const int fft_size = 256;
const float sample_rate = 1000.0f; // 1 kHz sampling rate
const int signal_len = fft_size;
int fft_test_pass = 1;
// Initialize FFT
printf("1. FFT Initialization:\n");
tiny_error_t ret = tiny_fft_init(fft_size);
if (ret != TINY_OK)
{
printf(" ✗ FFT initialization failed: %d\n", ret);
return;
}
printf(" ✓ FFT initialized (max size: %d)\n\n", fft_size);
// Generate test signal
float *input_signal = (float *)malloc(signal_len * sizeof(float));
float *fft_result = (float *)malloc(signal_len * 2 * sizeof(float)); // Complex output
float *magnitude = (float *)malloc(signal_len * sizeof(float));
float *power = (float *)malloc(signal_len * sizeof(float));
float *reconstructed = (float *)malloc(signal_len * sizeof(float));
if (!input_signal || !fft_result || !magnitude || !power || !reconstructed)
{
printf(" ✗ Memory allocation failed\n");
goto cleanup;
}
generate_test_signal(input_signal, signal_len, sample_rate);
printf("2. Test Signal Generation:\n");
printf(" Input: Signal with frequencies 15 Hz, 70 Hz, and 120 Hz\n");
printf(" Sample rate: %.1f Hz\n", sample_rate);
printf(" Signal length: %d samples\n", signal_len);
printf(" First 10 samples: ");
for (int i = 0; i < 10 && i < signal_len; i++)
{
printf("%.3f ", input_signal[i]);
}
printf("\n\n");
// Test FFT without window
printf("3. FFT (No Window):\n");
printf(" Input: Test signal (length=%d)\n", signal_len);
ret = tiny_fft_f32(input_signal, signal_len, fft_result, TINY_FFT_WINDOW_NONE);
if (ret != TINY_OK)
{
printf(" ✗ FFT failed: %d\n", ret);
goto cleanup;
}
printf(" ✓ FFT completed\n");
// Calculate magnitude
ret = tiny_fft_magnitude_f32(fft_result, signal_len, magnitude);
if (ret != TINY_OK)
{
printf(" ✗ Magnitude calculation failed\n");
goto cleanup;
}
// Calculate power spectrum
ret = tiny_fft_power_spectrum_f32(fft_result, signal_len, power);
if (ret != TINY_OK)
{
printf(" ✗ Power spectrum calculation failed\n");
goto cleanup;
}
printf(" Output: FFT result (complex, length=%d)\n", signal_len);
printf(" Magnitude spectrum: First 10 values: ");
for (int i = 0; i < 10 && i < signal_len; i++)
{
printf("%.3f ", magnitude[i]);
}
printf("\n\n");
// Find peak frequency
printf("4. Peak Frequency Detection:\n");
printf(" Input: Power spectrum (length=%d)\n", signal_len);
float peak_freq, peak_power;
ret = tiny_fft_find_peak_frequency(power, signal_len, sample_rate, &peak_freq, &peak_power);
if (ret != TINY_OK)
{
printf(" ✗ Peak detection failed\n");
goto cleanup;
}
printf(" Output: Peak frequency = %.2f Hz (power = %.3f)\n", peak_freq, peak_power);
printf(" Expected: strongest peak near ~15 Hz\n\n");
if (fabsf(peak_freq - 15.0f) > 5.0f)
{
fft_test_pass = 0;
}
// Find top frequencies
printf("5. Top Frequencies Detection:\n");
printf(" Input: Power spectrum (length=%d)\n", signal_len);
const int top_n = 3;
float *top_freqs = (float *)malloc(top_n * sizeof(float));
float *top_powers = (float *)malloc(top_n * sizeof(float));
if (!top_freqs || !top_powers)
{
printf(" ✗ Memory allocation failed\n");
goto cleanup;
}
ret = tiny_fft_find_top_frequencies(power, signal_len, sample_rate, top_n, top_freqs, top_powers);
if (ret != TINY_OK)
{
printf(" ✗ Top frequencies detection failed\n");
goto cleanup;
}
printf(" Output: Top %d frequencies:\n", top_n);
for (int i = 0; i < top_n; i++)
{
printf(" [%d] %.2f Hz (power = %.3f)\n", i + 1, top_freqs[i], top_powers[i]);
}
printf(" Expected: top frequencies should include ~15 Hz and ~70 Hz\n\n");
if (top_freqs[0] <= 0.0f || top_freqs[1] <= 0.0f)
{
fft_test_pass = 0;
}
// Test IFFT
printf("6. IFFT (Signal Reconstruction):\n");
printf(" Input: FFT result (complex, length=%d)\n", signal_len);
ret = tiny_fft_ifft_f32(fft_result, signal_len, reconstructed);
if (ret != TINY_OK)
{
printf(" ✗ IFFT failed: %d\n", ret);
goto cleanup;
}
printf(" Output: Reconstructed signal (length=%d)\n", signal_len);
printf(" First 10 samples: ");
for (int i = 0; i < 10 && i < signal_len; i++)
{
printf("%.3f ", reconstructed[i]);
}
printf("\n");
// Verify reconstruction (should match original, accounting for window effects)
float max_diff = 0.0f;
for (int i = 0; i < signal_len; i++)
{
float diff = fabsf(reconstructed[i] - input_signal[i]);
if (diff > max_diff)
{
max_diff = diff;
}
}
printf(" Max difference from original: %.6f\n", max_diff);
printf(" ✓ IFFT reconstruction completed\n\n");
if (max_diff > 1e-3f)
{
fft_test_pass = 0;
}
// Test with window
printf("7. FFT with Hanning Window:\n");
printf(" Input: Test signal (length=%d) with Hanning window\n", signal_len);
ret = tiny_fft_f32(input_signal, signal_len, fft_result, TINY_FFT_WINDOW_HANNING);
if (ret != TINY_OK)
{
printf(" ✗ Windowed FFT failed\n");
goto cleanup;
}
ret = tiny_fft_power_spectrum_f32(fft_result, signal_len, power);
if (ret != TINY_OK)
{
printf(" ✗ Power spectrum calculation failed\n");
goto cleanup;
}
ret = tiny_fft_find_peak_frequency(power, signal_len, sample_rate, &peak_freq, &peak_power);
if (ret != TINY_OK)
{
printf(" ✗ Peak detection failed\n");
goto cleanup;
}
printf(" Output: Peak frequency = %.2f Hz (power = %.3f)\n", peak_freq, peak_power);
printf(" Note: Window reduces spectral leakage, improving frequency resolution\n\n");
free(top_freqs);
free(top_powers);
cleanup:
if (input_signal)
free(input_signal);
if (fft_result)
free(fft_result);
if (magnitude)
free(magnitude);
if (power)
free(power);
if (reconstructed)
free(reconstructed);
// Deinitialize
tiny_fft_deinit();
printf("8. FFT Deinitialization:\n");
printf(" ✓ FFT deinitialized\n");
printf(" Result: %s\n", fft_test_pass ? "PASS" : "FAIL");
printf("\n========================================\n");
}
========== TinyFFT Test ==========
1. FFT Initialization:
✓ FFT initialized (max size: 256)
2. Test Signal Generation:
Input: Signal with frequencies 15 Hz, 70 Hz, and 120 Hz
Sample rate: 1000.0 Hz
Signal length: 256 samples
First 10 samples: 0.400 0.584 0.623 0.505 0.281 0.056 -0.065 -0.016 0.194 0.498
3. FFT (No Window):
Input: Test signal (length=256)
✓ FFT completed
Output: FFT result (complex, length=256)
Magnitude spectrum: First 10 values: 4.801 5.603 8.669 20.017 111.409 16.176 9.060 6.401 4.987 4.095
4. Peak Frequency Detection:
Input: Power spectrum (length=256)
Output: Peak frequency = 15.61 Hz (power = 48.484)
Expected: strongest peak near ~15 Hz
5. Top Frequencies Detection:
Input: Power spectrum (length=256)
Output: Top 3 frequencies:
[1] 15.61 Hz (power = 48.484)
[2] 70.32 Hz (power = 10.440)
[3] 120.99 Hz (power = 1.998)
Expected: top frequencies should include ~15 Hz and ~70 Hz
6. IFFT (Signal Reconstruction):
Input: FFT result (complex, length=256)
Output: Reconstructed signal (length=256)
First 10 samples: 0.400 0.584 0.623 0.505 0.281 0.056 -0.065 -0.016 0.194 0.498
Max difference from original: 0.000002
✓ IFFT reconstruction completed
7. FFT with Hanning Window:
Input: Test signal (length=256) with Hanning window
Output: Peak frequency = 15.29 Hz (power = 12.439)
Note: Window reduces spectral leakage, improving frequency resolution
8. FFT Deinitialization:
✓ FFT deinitialized
Result: PASS
========================================