TinyFFT — Tests and results¶
Validation target
This example validates FFT, spectral peak search and IFFT reconstruction for a synthetic 15 / 70 / 120 Hz signal. The original output and complete test sources remain below.
Historical result summary¶
Run date, device and firmware revision were not recorded; source-file dates do not establish the run date. Values below come from the original output.
| Item | Reference | Historical output | Interpretation |
|---|---|---|---|
| Peak 1 | 15 Hz | 15.61 Hz | Absolute error 0.61 Hz |
| Peak 2 | 70 Hz | 70.32 Hz | Absolute error 0.32 Hz |
| Peak 3 | 120 Hz | 120.99 Hz | Absolute error 0.99 Hz |
| IFFT | Original signal | Max difference 0.000002 | Reconstruction error in this run |
| Hann peak | 15 Hz | 15.29 Hz | Absolute error 0.29 Hz |
| Status | Test criteria | PASS | Applies to this run and these criteria |
Configuration, criteria and interpretation¶
The sample rate is fs = 1000 Hz, with N = 256 and bin spacing fs/N = 3.90625 Hz. Quadratic interpolation between neighboring spectral points produces fractional peak frequencies; two decimal places do not establish 0.01 Hz measurement accuracy.
The no-window peak check is |peak - 15| <= 5 Hz, the first two returned frequencies must be positive, and IFFT maximum error must be <= 1e-3. PASS does not mean all three peaks received strict error checks. The Hann window reduces leakage and this run's dominant peak is closer to the input. The original log's resolution claim is retained as history; it does not establish that the same transform length always resolves more closely spaced tones.
To reproduce, call tiny_fft_test() from middleware/tiny_dsp/transform/tiny_fft_test.c in the selected project. Confirm the startup path reaches it and retain configuration with complete serial output.
TEST RESULTS¶
========== 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
========================================
tiny_fft_test.h¶
/**
* @file tiny_fft_test.h
* @author SHUAIWEN CUI (SHUAIWEN001@e.ntu.edu.sg)
* @brief tiny_fft | test | header
* @version 1.0
* @date 2025-11-16
* @copyright Copyright (c) 2025
*
*/
#pragma once
/* DEPENDENCIES */
#include "tiny_fft.h"
#ifdef __cplusplus
extern "C"
{
#endif
void tiny_fft_test(void);
#ifdef __cplusplus
}
#endif
tiny_fft_test.c¶
Expand tiny_fft_test.c · 241 lines
/**
* @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");
}