Note
Aller à la fin pour télécharger le code complet de l’exemple.
Remplissage par zéro pour l’amélioration de la FFT#
Cet exemple montre comment appliquer un remplissage par zéro aux signaux, une technique courante utilisée pour améliorer la résolution en fréquence de la FFT. Il montre l’utilisation correcte de sigima.params.ZeroPadding1DParam, y compris l’appel important à update_from_obj().
Le remplissage par zéro ajoute des zéros à un signal, interpolant ainsi la représentation dans le domaine fréquentiel. Cela est particulièrement utile pour :
Amélioration de la résolution en fréquence dans l’analyse FFT
Préparation des signaux pour les opérations de convolution
Correspondance des longueurs de signal pour les comparaisons spectrales
Importation des modules requis#
import numpy as np
import sigima.params
import sigima.proc.signal as sips
from sigima import viz
from sigima.objects import create_signal
Créer un signal de test#
Nous créons un simple signal cosinus avec une fréquence spécifique.
# Signal parameters
freq = 50.0 # Hz
duration = 0.1 # seconds
sample_rate = 1000 # Hz
n_points = int(duration * sample_rate)
# Create time array and signal
t = np.linspace(0, duration, n_points, endpoint=False)
y = np.cos(2 * np.pi * freq * t)
signal = create_signal(
title=f"Cosine {freq} Hz", x=t, y=y, units=("s", "V"), labels=("Time", "Amplitude")
)
print(f"Original signal: {n_points} points")
viz.view_curves(signal, title="Original Signal")
Original signal: 100 points
Remplissage par zéro avec la stratégie « next_pow2 »#
La stratégie « next_pow2 » remplit le signal jusqu’à la prochaine puissance de 2, ce qui est optimal pour les calculs FFT.
Important
Lors de l’utilisation de stratégies autres que « custom », vous devez appeler update_from_obj() pour calculer le nombre de points de remplissage en fonction de la taille réelle du signal.
# Create the parameter with "next_pow2" strategy
param = sigima.params.ZeroPadding1DParam.create(strategy="next_pow2")
# At this point, param.n is still the default value (1)
print(f"Before update_from_obj: n = {param.n}")
# IMPORTANT: Update parameters from the signal to compute the actual 'n'
param.update_from_obj(signal)
# Now param.n has been computed based on the signal size
print(
f"After update_from_obj: n = {param.n} "
f"(signal will be padded to {n_points + param.n} points)"
)
# Apply zero-padding
padded_signal = sips.zero_padding(signal, param)
padded_size = padded_signal.y.size
power_of_2 = 2 ** int(np.log2(padded_size))
print(f"Padded signal: {padded_size} points (power of 2: {power_of_2})")
Before update_from_obj: n = 1
After update_from_obj: n = 28 (signal will be padded to 128 points)
Padded signal: 128 points (power of 2: 128)
Comparer les signaux originaux et remplis#
Le signal rempli a des zéros ajoutés à la fin.
viz.view_curves([signal, padded_signal], title="Original vs Zero-Padded Signal")
Comparaison FFT : résolution en fréquence améliorée#
Le remplissage par zéro améliore la résolution en fréquence apparente de la FFT en interpolant entre les bins de fréquence.
# Compute FFT of original signal
fft_original = sips.fft(signal)
fft_original.title = f"FFT Original ({fft_original.y.size} bins)"
# Compute FFT of padded signal
fft_padded = sips.fft(padded_signal)
fft_padded.title = f"FFT Zero-Padded ({fft_padded.y.size} bins)"
print(f"Original FFT: {fft_original.y.size} frequency bins")
print(f"Padded FFT: {fft_padded.y.size} frequency bins")
viz.view_curves([fft_original, fft_padded], title="FFT: Original vs Zero-Padded")
Original FFT: 100 frequency bins
Padded FFT: 128 frequency bins
Utilisation de différentes stratégies#
Les stratégies disponibles sont :
"next_pow2": Remplir jusqu’à la prochaine puissance de 2 (optimal pour la FFT)"double": Doubler la longueur du signal"triple": Tripler la longueur du signal"custom": Spécifier le nombre exact de points à ajouter
Strategy 'next_pow2': adds 28 points → total 128
Strategy 'double': adds 100 points → total 200
Strategy 'triple': adds 200 points → total 300
Utilisation de la stratégie « custom »#
Avec la stratégie « custom », vous spécifiez le nombre exact de points. Dans ce cas, update_from_obj() n’est pas strictement nécessaire (mais sans danger).
param_custom = sigima.params.ZeroPadding1DParam.create(strategy="custom", n=500)
print(f"Custom strategy: adds {param_custom.n} points")
padded_custom = sips.zero_padding(signal, param_custom)
print(f"Result: {padded_custom.y.size} points")
Custom strategy: adds 500 points
Result: 600 points
Choix de l’emplacement du remplissage#
Le remplissage par zéro peut être appliqué à différents emplacements :
"append": Ajouter des zéros à la fin (par défaut)"prepend": Ajouter des zéros au début"both": Répartir les zéros entre le début et la fin
from sigima.enums import PadLocation1D
results = []
for location in PadLocation1D:
param = sigima.params.ZeroPadding1DParam.create(strategy="double")
param.location = location
param.update_from_obj(signal)
result = sips.zero_padding(signal, param)
result.title = f"Padded ({location.value})"
results.append(result)
print(f"Location '{location.value}': x=[{result.x[0]:.4f}, {result.x[-1]:.4f}]")
viz.view_curves(results, title="Padding Location Comparison")
Location 'append': x=[0.0000, 0.1990]
Location 'prepend': x=[-0.1000, 0.0990]
Location 'both': x=[-0.0500, 0.1490]