0% found this document useful (0 votes)
3 views

SignalSystemAnalysis_pythonApplication

This document provides a comprehensive guide on using Python for signal processing, focusing on Fourier Series coefficients, numerical integration, and visualization techniques. It includes code examples for calculating Fourier coefficients, filtering signals, and performing convolution, along with interactive visualizations for better understanding. Additionally, it covers applications such as multipath channel analysis and spectrogram analysis.

Uploaded by

umusyucel3
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
3 views

SignalSystemAnalysis_pythonApplication

This document provides a comprehensive guide on using Python for signal processing, focusing on Fourier Series coefficients, numerical integration, and visualization techniques. It includes code examples for calculating Fourier coefficients, filtering signals, and performing convolution, along with interactive visualizations for better understanding. Additionally, it covers applications such as multipath channel analysis and spectrogram analysis.

Uploaded by

umusyucel3
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 15

Python Signal Processing Lab: Applications

1. Introduction & Objective

This lab demonstrates how to calculate Fourier Series coefficients numerically using Python
instead of manual integration. We'll use SciPy's integration capabilities to evaluate the
integrals and visualize the spectrum.
2. Numerical Integration with SciPy

import numpy as np

from scipy.integrate import quad

import matplotlib.pyplot as plt

# Define a simple function to integrate

def fcos(t):
return np.cos(np.pi*t)

# Perform numerical integration

result, error = quad(fcos, 0, 5)

print(f"Integration result: {result:.6f}, Error estimate: {error:.2e}")

# More complex example with absolute value


def exp_abs(t):

return np.exp(-np.abs(t))

result, error = quad(exp_abs, -2, 2)

print(f"Integration of e^(-|t|) from -2 to 2: {result:.6f}")

3. Fourier Series Coefficient Calculation

def fourier_coefficient(x, k, T0, num_points=1000):


"""

Calculate the k-th Fourier series coefficient for periodic function x(t)
"""
omega0 = 2*np.pi/T0

integrand = lambda t: x(t) * np.exp(-1j*k*omega0*t)

ak, _ = quad(integrand, -T0/2, T0/2)

return ak/T0

# Define a square wave function

def square_wave(t, T0=2*np.pi):


t_mod = t % T0

return 1 if t_mod < T0/2 else -1

# Calculate coefficients for k=-10 to 10

N = 10

T0 = 2*np.pi

coefficients = [fourier_coefficient(square_wave, k, T0) for k in range(-N, N+1)]

k_values = np.arange(-N, N+1)

# Plot the magnitude spectrum

plt.figure(figsize=(10, 5))

plt.stem(k_values, np.abs(coefficients))

plt.title('Fourier Series Coefficients for Square Wave')

plt.xlabel('k')

plt.ylabel('|a_k|')
plt.grid(True)

plt.show()

3.1. Interactive Square Wave Approximation

import numpy as np

import matplotlib.pyplot as plt


import matplotlib.animation as animation
from IPython.display import HTML

# Fourier Series Square Wave Approximation

def square_wave_fourier(t, N):

T0 = 2 * np.pi

result = np.zeros_like(t)

for k in range(1, N+1, 2): # Only odd harmonics

result += (4 / (np.pi * k)) * np.sin(k * (2 * np.pi / T0) * t)

return result

# Setting up the figure for animation

fig, ax = plt.subplots(figsize=(8, 5))

ax.set_xlim(-np.pi, np.pi)

ax.set_ylim(-1.5, 1.5)
ax.set_xlabel("t")

ax.set_ylabel("Signal")

ax.set_title("Fourier Series Approximation of Square Wave")

t_values = np.linspace(-np.pi, np.pi, 500)

line, = ax.plot([], [], lw=2, label="Fourier Approximation")

ax.legend()

# Update function for animation

def update(frame):

N = frame * 2 + 1 # Increase number of terms incrementally

y_values = square_wave_fourier(t_values, N)

line.set_data(t_values, y_values)
return line,
# Create animation

ani = animation.FuncAnimation(fig, update, frames=100, interval=300, repeat=True)

# Display animation

HTML(ani.to_jshtml())

4. Interactive Fourier Series Visualization

import numpy as np

import matplotlib.pyplot as plt

import ipywidgets as widgets

from IPython.display import display

T0 = 2 * np.pi

def square_wave(t, T0=2*np.pi):

return np.where((t % T0) < T0/2, 1, -1) # Vectorized for efficiency

def fourier_coefficient(k):

""" Analytical Fourier coefficients for a square wave """

if k == 0:

return 0 # No DC component
elif k % 2 == 1: # Only odd harmonics exist

return 4 / (np.pi * k)

else:

return 0 # Even harmonics are zero

def plot_fourier_approximation(N_terms=5):
t = np.linspace(-2*np.pi, 2*np.pi, 1000)
approximation = np.zeros_like(t)

for k in range(1, 2*N_terms, 2): # Only odd harmonics

ak = fourier_coefficient(k)

approximation += ak * np.sin(k * (2 * np.pi / T0) * t)

plt.figure(figsize=(10, 5))
plt.plot(t, approximation, label='Fourier approximation')

plt.plot(t, square_wave(t), '--', label='Original signal')

plt.title(f'Fourier Series Approximation with {2*N_terms-1} terms')

plt.legend()

plt.grid(True)

plt.show()

interactive_plot = widgets.interactive(plot_fourier_approximation,
N_terms=widgets.IntSlider(min=1, max=20, value=5))

display(interactive_plot)
Design with Fourier Series – Distortion

1. Filtering Periodic Signals

import numpy as np

import matplotlib.pyplot as plt

# Define the frequency response of the filter


def H(jw):

return (1j * jw) / ((4 - jw**2) + 1j * jw / 3)

# Define Fourier series parameters


T0 = 2 * np.pi
omega0 = 2 * np.pi / T0

k_values = np.arange(-10, 11) # Range of Fourier coefficients

# Generate Fourier coefficients (example function)

coefficients = [4 / (np.pi * k) if k % 2 == 1 else 0 for k in k_values] # Square wave Fourier


series

# Calculate output coefficients by filtering input coefficients

output_coeffs = [coeff * H(1j * k * omega0) for k, coeff in zip(k_values, coefficients)]

# Plot input and output spectra

plt.figure(figsize=(12, 5))

# Input Spectrum

plt.subplot(1, 2, 1)

plt.stem(k_values * omega0, np.abs(coefficients), markerfmt="ro", basefmt=" ")

plt.title("Input Spectrum")
plt.xlabel("Frequency (rad/s)")

plt.ylabel("|a_k|")

plt.grid(True)

# Output Spectrum After Filtering

plt.subplot(1, 2, 2)

plt.stem(k_values * omega0, np.abs(output_coeffs), markerfmt="bo", basefmt=" ")


plt.title("Output Spectrum After Filtering")

plt.xlabel("Frequency (rad/s)")

plt.ylabel("|b_k|")

plt.grid(True)
plt.tight_layout()

plt.show()

2. Time Domain Reconstruction

import numpy as np

import matplotlib.pyplot as plt

def reconstruct_signal(coeffs, t, T0, N):

omega0 = 2*np.pi/T0

signal = np.zeros_like(t, dtype=complex) # Ensure it's initialized properly

for k, ak in zip(range(-N, N+1), coeffs): # Pass N explicitly

signal += ak * np.exp(1j*k*omega0*t)

return np.real(signal)

# Define parameters
T0 = 2 * np.pi

N = len(coefficients) // 2 # Ensure correct range for coefficients

t = np.linspace(-T0, 2*T0, 1000)

# Reconstruct input and output signals

input_signal = reconstruct_signal(coefficients, t, T0, N)

output_signal = reconstruct_signal(output_coeffs, t, T0, N)

# Plot reconstructed signals

plt.figure(figsize=(10, 5))

plt.plot(t, input_signal, label='Input Square Wave', color='blue')

plt.plot(t, output_signal, label='Filtered Output', color='red')

plt.title('Time Domain Signals Before and After Filtering')


plt.legend()
plt.grid(True)

plt.show()

2. Fast Forier Transform (FFT) Visuilization

import numpy as np

import matplotlib.pyplot as plt

import ipywidgets as widgets


from IPython.display import display

# Ensure correct backend for plotting

%matplotlib inline

# Sampling parameters

Fs = 1000 # Sampling frequency (Hz)

T = 1.0 # Duration (seconds)


t = np.linspace(0, T, int(Fs*T), endpoint=False) # Time array

def generate_signal(freq1=50, freq2=150, freq3=300):

""" Generate a signal composed of multiple sine waves """

return np.sin(2*np.pi*freq1*t) + np.sin(2*np.pi*freq2*t) + np.sin(2*np.pi*freq3*t)

def plot_fft(freq1=50, freq2=150, freq3=300):


""" Compute and plot FFT of the generated signal """

signal = generate_signal(freq1, freq2, freq3)

N = len(signal) # Number of samples

# Compute FFT

fft_values = np.fft.fft(signal)
freqs = np.fft.fftfreq(N, 1/Fs)
# Only take positive frequencies

mask = freqs >= 0 # Fixed issue: previously filtering only greater than zero

# Plot time-domain signal

plt.figure(figsize=(12, 5))

plt.subplot(1, 2, 1)
plt.plot(t, signal, label="Time-domain Signal", color="royalblue")

plt.xlabel("Time (s)")

plt.ylabel("Amplitude")

plt.title("Original Signal")

plt.legend()

plt.grid(True)

# Plot FFT magnitude spectrum with enhanced visuals


plt.subplot(1, 2, 2)

plt.stem(freqs[mask], np.abs(fft_values[mask]), markerfmt="ro")

plt.xlabel("Frequency (Hz)")

plt.ylabel("Magnitude")

plt.title("Frequency-domain (FFT) Spectrum")

plt.grid(True)

plt.tight_layout()

plt.show()

# Use interactive sliders with improved functionality for Colab

interactive_fft = widgets.interactive(plot_fft,

freq1=widgets.IntSlider(min=10, max=500, value=50, step=10),


freq2=widgets.IntSlider(min=10, max=500, value=150, step=10),
freq3=widgets.IntSlider(min=10, max=500, value=300, step=10))

display(interactive_fft)

Convolution Visualization in Python

1. Discrete-Time Convolution

import numpy as np
import matplotlib.pyplot as plt

def discrete_convolution(x, h):

N = len(x)

M = len(h)

y = np.zeros(N + M - 1)

for n in range(len(y)):
for k in range(max(0, n-M+1), min(n+1, N)):

y[n] += x[k] * h[n-k]

return y

# Create a pulse signal

x = np.concatenate([np.zeros(5), np.ones(10), np.zeros(5)])

# Create a moving average filter


h = np.ones(5)/5

y = discrete_convolution(x, h)

plt.figure(figsize=(12, 4))

plt.subplot(1, 3, 1)
plt.stem(x, markerfmt="ro") # Removed use_line_collection=True
plt.title("Input Signal x[n]")

plt.subplot(1, 3, 2)

plt.stem(h, markerfmt="bo") # Removed use_line_collection=True

plt.title("Impulse Response h[n]")

plt.subplot(1, 3, 3)
plt.stem(y, markerfmt="go") # Removed use_line_collection=True

plt.title("Output y[n] = x[n] * h[n]")

plt.tight_layout()

plt.show()

2. Continuous-Time Convolution

from scipy.signal import convolve

# Define time axis

t = np.linspace(0, 10, 1000)

# Create exponential signals

x = np.exp(-0.25*t) * (t <= 6)

h = np.exp(-t) * ((t >= -1) & (t <= 5)) # Shifted to include negative time

# Perform convolution

y = convolve(x, h, mode='same') * (t[1]-t[0]) # Multiply by dt for proper scaling

plt.figure(figsize=(12, 4))

plt.subplot(1, 3, 1)
plt.plot(t, x)
plt.title('Input Signal x(t)')

plt.subplot(1, 3, 2)

plt.plot(t, h)

plt.title('Impulse Response h(t)')

plt.subplot(1, 3, 3)
plt.plot(t, y)

plt.title('Output y(t) = x(t)*h(t)')

plt.tight_layout()

plt.show()

3. Interactive Convolution Visualization

from matplotlib.animation import FuncAnimation

from IPython.display import HTML

def animate_convolution(x, h, t):

fig, (ax1, ax2, ax3) = plt.subplots(3, 1, figsize=(10, 8))

# Plot original signals

ax1.plot(t, x, label='x(τ)')

ax1.set_title('Input Signal x(τ)')


ax1.legend()

ax2.plot(t, h, label='h(τ)')

ax2.set_title('Impulse Response h(τ)')

ax2.legend()

# Initialize output plot


line, = ax3.plot([], [], 'r-')

ax3.set_xlim(t[0], t[-1])

ax3.set_ylim(0, 1.1*np.max(convolve(x, h, mode='same')))

ax3.set_title('Convolution Result y(t) = x(t)*h(t)')

# Calculate full convolution result

full_conv = convolve(x, h, mode='same') * (t[1]-t[0])

def update(frame):

# Flip and shift h

shifted_h = np.roll(h[::-1], frame)

# Clear and redraw

ax1.clear()

ax1.plot(t, x, label='x(τ)')
ax1.plot(t, shifted_h, 'g--', label=f'h({frame}-τ)')

ax1.set_title(f'Time t = {t[frame]:.2f}')

ax1.legend()

ax2.clear()

ax2.plot(t, shifted_h, 'g--', label=f'h({frame}-τ)')

ax2.set_title('Flipped and Shifted Impulse Response')


ax2.legend()

# Update convolution result up to current frame

line.set_data(t[:frame], full_conv[:frame])

return line,

ani = FuncAnimation(fig, update, frames=len(t), interval=50, blit=True)


plt.close()

return HTML(ani.to_jshtml())

# Create signals

t = np.linspace(0, 10, 100)

x = np.exp(-0.5*t) * (t <= 5)

h = np.exp(-2*t) * (t <= 3)

animate_convolution(x, h, t)

Other Applications

1. Multipath Channel Analysis

def multipath_signal(f0=1000, delays=[0, 0.001, 0.002], amps=[1, 0.7, 0.3], dur=0.01,


fs=44100):

t = np.arange(0, dur, 1/fs)

signal = np.zeros_like(t)

for delay, amp in zip(delays, amps):

signal += amp * np.cos(2*np.pi*f0*(t - delay))

return t, signal

t, sig = multipath_signal()
plt.figure(figsize=(10, 4))

plt.plot(t, sig)

plt.title('Multipath Signal Composition')

plt.xlabel('Time (s)')
plt.ylabel('Amplitude')
plt.grid(True)

plt.show()

2. Spectrogram Analysis

from scipy.signal import spectrogram

# Create a signal with time-varying frequency


fs = 10000

t = np.arange(0, 1, 1/fs)

f_inst = 100 + 800*t # Frequency sweeps from 100Hz to 900Hz

x = np.cos(2*np.pi*f_inst*t)

# Compute spectrogram

f, t_spec, Sxx = spectrogram(x, fs, nperseg=1024)

plt.figure(figsize=(10, 5))

plt.pcolormesh(t_spec, f, 10*np.log10(Sxx), shading='gouraud')

plt.colorbar(label='Power Spectral Density (dB)')

plt.ylabel('Frequency [Hz]')

plt.xlabel('Time [sec]')

plt.title('Spectrogram of Frequency-Swept Signal')

plt.show()

You might also like