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")
Qt widget 1
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")
Qt widget 1

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")
Qt widget 1
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

for strategy in ["next_pow2", "double", "triple"]:
    param = sigima.params.ZeroPadding1DParam.create(strategy=strategy)
    param.update_from_obj(signal)
    print(f"Strategy '{strategy}': adds {param.n} points → total {n_points + param.n}")
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")
Qt widget 1
Location 'append': x=[0.0000, 0.1990]
Location 'prepend': x=[-0.1000, 0.0990]
Location 'both': x=[-0.0500, 0.1490]

Galerie générée par Sphinx-Gallery