Podręcznik
3. Cyfrowe przetwarzanie sygnałów
3.3. Próbkowanie sygnałów w języku Python
Celem poniższych ćwiczeń jest zapoznanie się z podstawowymi poleceniami języka Python, które umożliwiają poprawne próbkowanie sygnałów ciągłych oraz generowanie ich przebiegów czasowych.
Poniższe przykłady przedstawiają pełne kody źródłowe w języku Python, opracowane na potrzeby realizacji poszczególnych zadań. Do wykonania przedstawionych zadań wymagane jest wykorzystanie następujących bibliotek języka Python:
import numpy as np # obliczenia numeryczne i tablice
import matplotlib.pyplot as plt # tworzenie wykresów ciągłych i dyskretnych
import scipy.signal as signal # generowanie standardowych sygnałów,
# takich jak np. prostokątny, trójkątny, piłokształtny
oraz wymagana jest znajomość między innymi funkcji:
sawtooth # generowanie sygnału piłokształtnego (scipy.signal)
np.interp # interpolacja liniowa jednowymiarowych danych (NumPy)
Do rekonstrukcji sygnałów wykorzystano metodę interpolacji Sinc, będącą jedną z najdokładniejszych metod rekonstrukcji w teorii cyfrowego przetwarzania sygnałów. W poniższych implementacjach została ona zdefiniowana w postaci funkcji:
sinc_interp()
Poniżej znajdują się ćwiczenia z przykładową implementacją. Autor zachęca czytelnika do realizacji poniższych kodów oraz do analizy różnych parametrów próbkowania sygnałów, poprzez ich modyfikację.
Z wykorzystaniem interpolacji, dokonać rekonstrukcji ciągłego sygnału piłokształtnego o częstotliwości \(5\) Hz i o czasie trwania \(1\) s. Przyjąć dwie częstotliwości próbkowania: \(40\) Hz i \(6\) Hz.
import numpy as np
import matplotlib.pyplot as plt
from scipy.signal import sawtooth
# Parametry
f = 5 # częstotliwość sygnału [Hz]
T = 1 # czas obserwacji [s]
t_cont = np.linspace(0, T, 2000) # czas ciągły
# Sygnał piłokształtny
x_cont = sawtooth(2 * np.pi * f * t_cont)
# Próbkowanie
fs_good = 40
fs_bad = 6
t_good = np.arange(0, T, 1/fs_good)
x_good = sawtooth(2 * np.pi * f * t_good)
t_bad = np.arange(0, T, 1/fs_bad)
x_bad = sawtooth(2 * np.pi * f * t_bad)
# Rekonstrukcja przez interpolację sinc
def sinc_interp(x, t, t_new):
T = t[1] - t[0]
sinc_matrix = np.sinc((t_new[:, None] - t[None, :]) / T)
return np.dot(sinc_matrix, x)
x_rec_good = sinc_interp(x_good, t_good, t_cont)
x_rec_bad = sinc_interp(x_bad, t_bad, t_cont)
# Wykresy
fig, axs = plt.subplots(2, 1, figsize=(12, 6), sharex=False)
# Poprawna rekonstrukcja
axs[0].plot(t_cont, x_cont, color='gray', label='Sygnał ciągły')
axs[0].plot(t_cont, x_rec_good, 'C0--', label=rf"Rekonstrukcja ($f_s = {fs_good}$ Hz)")
axs[0].stem(t_good, x_good, linefmt='C0-', markerfmt='C0o', basefmt=" ", label='Próbki')
axs[0].set_title('Poprawne próbkowanie i rekonstrukcja')
axs[0].set_xlabel('Czas [s]')
axs[0].legend(loc='upper right')
axs[0].set_ylabel('Amplituda')
axs[0].grid(True)
# Błędna rekonstrukcja (aliasing)
axs[1].plot(t_cont, x_cont, color='gray', label='Sygnał ciągły')
axs[1].plot(t_cont, x_rec_bad, 'C1--', label=rf"Rekonstrukcja ($f_s = {fs_bad}$ Hz)")
axs[1].stem(t_bad, x_bad, linefmt='C1-', markerfmt='C1o', basefmt=" ", label='Próbki')
axs[1].set_title('Aliasing – zła rekonstrukcja')
axs[1].set_xlabel('Czas [s]')
axs[1].set_ylabel('Amplituda')
axs[1].legend()
axs[1].grid(True)
plt.tight_layout()
plt.show()

Z wykorzystaniem interpolacji, dokonać rekonstrukcji ciągłego sygnału \(\mathrm{Sinc}(30t)\) o czasie trwania \(1\) s. Przyjąć dwie częstotliwości próbkowania: \(60\) Hz i \(10\) Hz.
import numpy as np
import matplotlib.pyplot as plt
# Parametry
T = 1 # czas obserwacji [s]
t_cont = np.linspace(-T/2, T/2, 2000) # czas ciągły
x_cont = np.sinc(30 * t_cont)
# Próbkowanie
fs_good = 60
fs_bad = 10
# Sygnał sinc
t_good = np.arange(-T/2, T/2, 1/fs_good)
x_good = np.sinc(30 * t_good)
t_bad = np.arange(-T/2, T/2, 1/fs_bad)
x_bad = np.sinc(30 * t_bad)
# Interpolacja sinc
def sinc_interp(x, t, t_new):
T = t[1] - t[0]
return np.dot(np.sinc((t_new[:, None] - t[None, :]) / T), x)
x_rec_good = sinc_interp(x_good, t_good, t_cont)
x_rec_bad = sinc_interp(x_bad, t_bad, t_cont)
# Wykresy
fig, axs = plt.subplots(2, 1, figsize=(12, 6), sharex=False)
# Poprawna rekonstrukcja
axs[0].plot(t_cont, x_cont, color='gray', label=rf'Sygnał ciągły $sinc(30t)$')
axs[0].plot(t_cont, x_rec_good, 'C0--', label=rf'Rekonstrukcja ($f_s = {fs_good}$ Hz)')
axs[0].stem(t_good, x_good, linefmt='C0-', markerfmt='C0o', basefmt=" ", label='Próbki')
axs[0].set_title('Poprawne próbkowanie i rekonstrukcja')
axs[0].set_xlabel('Czas [s]')
axs[0].legend(loc='upper right')
axs[0].set_ylabel('Amplituda')
axs[0].grid(True)
# Błędna rekonstrukcja (aliasing)
axs[1].plot(t_cont, x_cont, color='gray', label=rf'Sygnał ciągły $sinc(30t)$')
axs[1].plot(t_cont, x_rec_bad, 'C1--', label=rf'Rekonstrukcja ($f_s = {fs_bad}$ Hz)')
axs[1].stem(t_bad, x_bad, linefmt='C1-', markerfmt='C1o', basefmt=" ", label='Próbki')
axs[1].set_title('Aliasing – zła rekonstrukcja')
axs[1].set_xlabel('Czas [s]')
axs[1].set_ylabel('Amplituda')
axs[1].legend()
axs[1].grid(True)
plt.tight_layout()
plt.show()

Z wykorzystaniem interpolacji, dokonać rekonstrukcji ciągłego sygnału:
Przyjąć dwie częstotliwości próbkowania: \(120\) Hz i \(30\) Hz.
import numpy as np
import matplotlib.pyplot as plt
# Sygnał: suma sinusów (5 Hz + 25 Hz + 40 Hz)
def sum_signal(t):
return (
np.sin(2 * np.pi * 5 * t) + # niska
0.6 * np.sin(2 * np.pi * 25 * t) + # średnia
0.4 * np.sin(2 * np.pi * 40 * t) # wysoka (aliasowana!)
)
# Parametry
T = 2 # czas obserwacji [s]
t_cont = np.linspace(0, T, int(1000 * T), endpoint=False) # czas ciągły
x_cont = sum_signal(t_cont)
# Interpolacja sinc
def sinc_interp(x, t, t_new):
T = t[1] - t[0]
return np.dot(np.sinc((t_new[:, None] - t[None, :]) / T), x)
# Próbkowanie
fs_good = 120
fs_bad = 30
# Sygnały
t_good = np.arange(0, T, 1/fs_good)
x_good = np.interp(t_good, t_cont, x_cont) # do symulacju próbkowania
t_bad = np.arange(0, T, 1/fs_bad)
x_bad = np.interp(t_bad, t_cont, x_cont) # do symulacju próbkowania
t_dense = np.linspace(0, T, 5000)
x_rec_good = sinc_interp(x_good, t_good, t_dense)
x_rec_bad = sinc_interp(x_bad, t_bad, t_dense)
# Wykresy
fig, axs = plt.subplots(2, 1, figsize=(12, 6), sharex=False)
# Poprawna rekonstrukcja
axs[0].plot(t_cont, x_cont, color='gray', label=rf'Sygnał ciągły')
axs[0].plot(t_dense, x_rec_good, 'C0--', label=rf'Rekonstrukcja ($f_s = {fs_good}$ Hz)')
axs[0].stem(t_good, x_good, linefmt='C0-', markerfmt='C0o', basefmt=" ", label='Próbki')
axs[0].set_title('Poprawne próbkowanie i rekonstrukcja')
axs[0].set_xlabel('Czas [s]')
axs[0].legend(loc='upper right')
axs[0].set_ylabel('Amplituda')
axs[0].grid(True)
# Błędna rekonstrukcja (aliasing)
axs[1].plot(t_cont, x_cont, color='gray', label=rf'Sygnał ciągły')
axs[1].plot(t_dense, x_rec_bad, 'C1--', label=rf'Rekonstrukcja ($f_s = {fs_bad}$ Hz)')
axs[1].stem(t_bad, x_bad, linefmt='C1-', markerfmt='C1o', basefmt=" ", label='Próbki')
axs[1].set_title('Aliasing – zła rekonstrukcja')
axs[1].set_xlabel('Czas [s]')
axs[1].set_ylabel('Amplituda')
axs[1].legend()
axs[1].grid(True)
plt.tight_layout()
plt.show()
