How to process signals with scipy.signal in Python

How to process signals with scipy.signal in Python

Signal processing is a field that deals with the manipulation of signals to extract useful information or to enhance the signal quality. At its core, it involves understanding how signals can be represented, transformed, and analyzed. Signals can be anything from audio waves to sensor data, and processing them often requires a solid grasp of the underlying mathematics.

One foundational concept in signal processing is the representation of signals in both the time and frequency domains. The time domain shows how a signal varies over time, while the frequency domain reveals the different frequency components that make up the signal. A common tool used to switch between these domains is the Fourier Transform.

import numpy as np
import matplotlib.pyplot as plt

# Create a sample signal
fs = 1000  # Sampling frequency
t = np.linspace(0, 1, fs)
signal = np.sin(2 * np.pi * 50 * t) + np.sin(2 * np.pi * 120 * t)

# Compute the Fourier Transform
frequencies = np.fft.fftfreq(len(signal), 1/fs)
spectrum = np.fft.fft(signal)

# Plot the signal and its spectrum
plt.subplot(2, 1, 1)
plt.plot(t, signal)
plt.title('Time Domain Signal')
plt.subplot(2, 1, 2)
plt.plot(frequencies, np.abs(spectrum))
plt.title('Frequency Domain Spectrum')
plt.show()

The Fourier Transform decomposes a signal into its constituent frequencies, allowing us to see which frequencies are present and their magnitudes. This can be extremely useful in various applications such as audio processing, communications, and even medical imaging.

Another important aspect of signal processing is filtering. Filters are used to remove unwanted components from a signal or to enhance certain aspects. For example, a low-pass filter allows low frequencies to pass while attenuating higher frequencies. This can be implemented using digital filters, often designed using methods that ensure stability and optimal performance.

from scipy.signal import butter, lfilter

# Butterworth low-pass filter design
def butter_lowpass(cutoff, fs, order=5):
    nyq = 0.5 * fs
    normal_cutoff = cutoff / nyq
    b, a = butter(order, normal_cutoff, btype='low', analog=False)
    return b, a

# Apply the filter to the signal
def lowpass_filter(data, cutoff, fs, order=5):
    b, a = butter_lowpass(cutoff, fs, order=order)
    y = lfilter(b, a, data)
    return y

# Parameters
cutoff = 60  # Desired cutoff frequency of the filter, Hz
filtered_signal = lowpass_filter(signal, cutoff, fs)

In this example, we designed a low-pass Butterworth filter to remove high-frequency noise from our signal. The butter function from the scipy.signal module creates the filter coefficients, while the lfilter function applies the filter to the signal. This approach is quite powerful, as it allows for real-time signal processing in various applications.

Understanding these fundamentals lays the groundwork for more complex operations in signal processing. As you dive deeper, you’ll encounter more advanced concepts such as wavelets, adaptive filtering, and even machine learning techniques that can further enhance signal analysis. The journey through signal processing is both challenging and rewarding, opening up a range of possibilities for innovation and problem-solving in engineering and data science.

Applying filters and transformations with scipy.signal

One of the key advantages of using the scipy.signal module is its extensive set of functions for designing and applying various types of filters. Beyond low-pass filters, you can also implement high-pass, band-pass, and band-stop filters, each serving different purposes in signal manipulation. For instance, a high-pass filter can be used to remove low-frequency noise, which is particularly useful in applications like audio processing, where you may want to eliminate rumble or hum.

from scipy.signal import butter, lfilter

# Butterworth high-pass filter design
def butter_highpass(cutoff, fs, order=5):
    nyq = 0.5 * fs
    normal_cutoff = cutoff / nyq
    b, a = butter(order, normal_cutoff, btype='high', analog=False)
    return b, a

# Apply the high-pass filter to the signal
def highpass_filter(data, cutoff, fs, order=5):
    b, a = butter_highpass(cutoff, fs, order=order)
    y = lfilter(b, a, data)
    return y

# Parameters
cutoff_high = 70  # Desired cutoff frequency of the high-pass filter, Hz
high_filtered_signal = highpass_filter(signal, cutoff_high, fs)

In this code snippet, we create a high-pass Butterworth filter using a similar approach as the low-pass filter. The butter_highpass function generates the filter coefficients, while the highpass_filter function applies the filter to the signal. By adjusting the cutoff frequency, you can precisely target the frequency range you wish to preserve.

Transformations are another crucial aspect of the scipy.signal module. One common transformation is the Short-Time Fourier Transform (STFT), which allows you to analyze how the frequency content of a signal changes over time. That’s particularly useful for non-stationary signals, such as music or speech, where different frequencies may dominate at different times.

from scipy.signal import stft

# Compute the Short-Time Fourier Transform
frequencies, times, Zxx = stft(signal, fs=fs, nperseg=256)

# Plot the STFT magnitude
plt.pcolormesh(times, frequencies, np.abs(Zxx), shading='gouraud')
plt.title('STFT Magnitude')
plt.ylabel('Frequency [Hz]')
plt.xlabel('Time [sec]')
plt.colorbar()
plt.show()

The STFT provides a time-frequency representation of the signal, revealing how the frequency components evolve over time. This visualization can be instrumental in identifying patterns or anomalies in complex signals.

Another powerful tool in the scipy.signal module is the ability to design and apply various window functions, which can help mitigate spectral leakage when performing Fourier transforms. Common window functions include Hamming, Hanning, and Blackman windows. Using a window can improve the accuracy of your frequency analysis, especially when dealing with finite-length signals.

from scipy.signal import get_window

# Apply a Hamming window
window = get_window('hamming', 256)
windowed_signal = signal[:256] * window

# Compute and plot the FFT of the windowed signal
spectrum_windowed = np.fft.fft(windowed_signal)
plt.plot(np.abs(spectrum_windowed))
plt.title('FFT of Windowed Signal')
plt.xlabel('Frequency Bin')
plt.ylabel('Magnitude')
plt.show()

In this example, we apply a Hamming window to the first 256 samples of our signal before computing its FFT. The window helps to reduce the discontinuities at the edges of the segment, leading to a more accurate frequency representation.

As you explore these various functionalities within scipy.signal, you’ll find that they can be combined in a high number of ways to suit your specific signal processing needs. Whether you’re cleaning up noisy data, analyzing frequency content, or applying advanced techniques like wavelet transforms, the tools provided by scipy.signal are invaluable for any signal processing task.

Comments

No comments yet. Why don’t you start the discussion?

Leave a Reply

Your email address will not be published. Required fields are marked *