LAB 1
# LAB 1
import numpy as np
import matplotlib.pyplot as plt
def generate_exponential_damped_sinusoid(alpha, freq, length,
sample_rate=1000):
t = np.linspace(0, length, int(sample_rate * length),
endpoint=False)
signal = np.exp(-alpha * t) * np.sin(2 * np.pi * freq * t)
return t, signal
def generate_sawtooth(freq, length, sample_rate=1000, width=1.0):
t = np.linspace(0, length, int(sample_rate * length),
endpoint=False)
signal = 2 * (t * freq - np.floor(t * freq + 0.5)) # Normalized
between -1 and 1
return t, signal
def generate_triangular_pulse(freq, length, sample_rate=1000,
width=0.5):
t = np.linspace(0, length, int(sample_rate * length),
endpoint=False)
signal = 2 * np.abs(2 * (t * freq - np.floor(t * freq + 0.5))) - 1
return t, signal
# Parameters for the signals
duration = 1.0 # 1 second duration
sample_rate = 1000 # 1000 samples per second
# Generate signals
t_exp, exp_signal = generate_exponential_damped_sinusoid(alpha=2,
freq=5, length=duration, sample_rate=sample_rate)
t_saw, saw_signal = generate_sawtooth(freq=2, length=duration,
sample_rate=sample_rate)
t_tri, tri_signal = generate_triangular_pulse(freq=1, length=duration,
sample_rate=sample_rate)
# Plot the signals
plt.figure(figsize=(15, 5))
# Exponentially damped sinusoid
plt.subplot(1, 3, 1)
plt.plot(t_exp, exp_signal)
plt.title('Exponentially Damped Sinusoid')
plt.xlabel('Time (s)')
plt.ylabel('Amplitude')
plt.grid(True)
# Sawtooth waveform
plt.subplot(1, 3, 2)
plt.plot(t_saw, saw_signal)
plt.title('Sawtooth Waveform')
plt.xlabel('Time (s)')
plt.ylabel('Amplitude')
plt.grid(True)
# Triangular pulse
plt.subplot(1, 3, 3)
plt.plot(t_tri, tri_signal)
plt.title('Triangular Pulse')
plt.xlabel('Time (s)')
plt.ylabel('Amplitude')
plt.grid(True)
plt.tight_layout()
plt.show()
LAB 2
# LAB 2
import numpy as np
import matplotlib.pyplot as plt
def analyze_nyquist(F, Fs, duration=1.0):
t = np.linspace(0, duration, int(Fs * duration), endpoint=False)
analog_signal = np.sin(2 * np.pi * F * t)
sampled_t = np.arange(0, duration, 1/Fs)
sampled_signal = np.sin(2 * np.pi * F * sampled_t)
plt.figure(figsize=(12, 8))
plt.subplot(2, 1, 1)
plt.plot(t, analog_signal, label='Analog Signal')
plt.stem(sampled_t, sampled_signal, linefmt='r-', markerfmt='ro',
basefmt=' ', label='Sampled Points')
plt.title(f'F = {F} Hz, Fs = {Fs} Hz')
plt.xlabel('Time (s)')
plt.ylabel('Amplitude')
plt.legend()
plt.grid(True)
plt.subplot(2, 1, 2)
fft_freq = np.fft.fftfreq(len(analog_signal), 1/Fs)
fft_values = np.abs(np.fft.fft(analog_signal))
plt.plot(fft_freq, fft_values)
plt.title('Frequency Spectrum')
plt.xlabel('Frequency (Hz)')
plt.ylabel('Magnitude')
plt.grid(True)
plt.xlim(0, 3*F)
plt.tight_layout()
plt.show()
F = float(input("Enter the signal frequency (F): "))
Fs = float(input("Enter the sampling frequency (Fs): "))
analyze_nyquist(F, Fs)
Enter the signal frequency (F): 50
Enter the sampling frequency (Fs): 20
LAB 3
# Lab 3 Part 1 #
import numpy as np
import matplotlib.pyplot as plt
t = np.linspace(-10, 10, 25)
x1 = lambda t: np.sin(t)
x2 = lambda t: np.cos(t)
def checkLinearity(Tr):
x1plusx2 = lambda t: x1(t)+x2(t)
plt.plot(t, Tr(x1)(t)+Tr(x2)(t),'b', label='T(x1)+T(x2)')
plt.plot(t, Tr(x1plusx2)(t),'r.', label='T(x1+x2)')
plt.legend()
plt.title('Linearity Check')
plt.grid()
plt.show()
def Tr(x):
return lambda t: x(t-2) # y(t) = x(t-2)
checkLinearity(Tr)
def Tr(x):
return lambda t: x(t)**2 # y(t) = x(t)²
checkLinearity(Tr)
# LAB 3 Part 2
t = np.linspace(-2, 4, 5)
x = lambda t: np.sin(t)
def showTimeInvariance(Tr):
plt.figure(figsize=(10,4))
plt.subplot(1,2,1)
plt.plot(t, x(t),'r', label='x(t)')
plt.plot(t, x(t-0.5),'b', label='x(t-0.5)')
plt.plot(t, x(t-1),'k', label='x(t-1)')
plt.title('Input Signals')
plt.legend()
plt.grid()
plt.subplot(1,2,2)
y1 = Tr(x)
y2 = Tr(lambda t: x(t-0.5))
y3 = Tr(lambda t: x(t-1))
plt.plot(t, y1(t), 'r', label='T(x(t))')
plt.plot(t, y2(t), 'b', label='T(x(t-0.5))')
plt.plot(t, [y1(ti-0.5) for ti in t], 'b--', label='y(t-0.5)')
plt.plot(t, y3(t), 'k', label='T(x(t-1))')
plt.plot(t, [y1(ti-1) for ti in t], 'k--', label='y(t-1)')
plt.title('Output Responses')
plt.legend()
plt.grid()
plt.show()
def Tr(x):
return lambda t: t*x(t) # y(t) = t·x(t)
showTimeInvariance(Tr)
def Tr(x):
return lambda t: x(t)**2 # y(t) = x(t)²
showTimeInvariance(Tr)
LAB 4
# Lab 4
import numpy as np
import matplotlib.pyplot as plt
# Set smaller default figure size
plt.rcParams['figure.figsize'] = (10, 3)
def plot_signals(x, h, y, title):
plt.figure()
plt.subplot(1, 3, 1)
plt.stem(x, markerfmt='bo')
plt.title('x(n)')
plt.grid(True, alpha=0.3)
plt.subplot(1, 3, 2)
plt.stem(h, markerfmt='ro')
plt.title('h(n)')
plt.grid(True, alpha=0.3)
plt.subplot(1, 3, 3)
plt.stem(y, markerfmt='go')
plt.title('y(n) = x(n)*h(n)')
plt.grid(True, alpha=0.3)
plt.suptitle(title, y=1.05) # Adjust title position
plt.tight_layout()
plt.show()
# Case 1
x1 = np.array([-3, -2, -1, 0, 1, 2, 3])
h1 = np.array([1, 1, 1, 1, 1])
y1 = np.convolve(x1, h1, mode='full')
plot_signals(x1, h1, y1, "Case 1: x(n) = { -3,-2,-1,0,1,2,3 }, h(n) =
{1,1,1,1,1}")
# Case 2
x2 = np.array([1, 2, 3, 4, 5])
h2 = np.array([1])
y2 = np.convolve(x2, h2, mode='full')
plot_signals(x2, h2, y2, "Case 2: x(n) = {1,2,3,4,5}, h(n) = {1}")
# Case 3
x3 = np.array([-1, -2, 0, 2, 1])
h3 = x3.copy()
y3 = np.convolve(x3, h3, mode='full')
plot_signals(x3, h3, y3, "Case 3: x(n) = { -1,-2,0,2,1}, h(n) = x(n)")
LAB 5
# Lab 5
import numpy as np
import matplotlib.pyplot as plt
# Set smaller figure sizes
plt.rcParams['figure.figsize'] = (8, 3)
# Task 1: DFT of f[n] = {1, 1, -1, -1}
f = np.array([1, 1, -1, -1])
F = np.fft.fft(f)
# Plot for Task 1
plt.figure()
plt.subplot(1, 2, 1)
plt.stem(np.abs(F))
plt.title('Magnitude (Task 1)')
plt.xlabel('k')
plt.grid(True)
plt.subplot(1, 2, 2)
plt.stem(np.angle(F))
plt.title('Phase (Task 1)')
plt.xlabel('k')
plt.grid(True)
plt.tight_layout()
plt.show()
# Task 2: IDFT of given signals
# Case a
Fa = np.array([0, 2-2j, 0, 2+2j])
fa = np.fft.ifft(Fa)
# Case b
Fb = np.array([30, 1-1j, 1, 1+1j])
fb = np.fft.ifft(Fb)
# Plot for Task 2a
plt.figure()
plt.subplot(1, 2, 1)
plt.stem(np.abs(Fa))
plt.title('Magnitude (Task 2a)')
plt.xlabel('k')
plt.grid(True)
plt.subplot(1, 2, 2)
plt.stem(np.angle(Fa))
plt.title('Phase (Task 2a)')
plt.xlabel('k')
plt.grid(True)
plt.tight_layout()
plt.show()
# Plot for Task 2b
plt.figure()
plt.subplot(1, 2, 1)
plt.stem(np.abs(Fb))
plt.title('Magnitude (Task 2b)')
plt.xlabel('k')
plt.grid(True)
plt.subplot(1, 2, 2)
plt.stem(np.angle(Fb))
plt.title('Phase (Task 2b)')
plt.xlabel('k')
plt.grid(True)
plt.tight_layout()
plt.show()
# Print results compactly
print("Task 1 - DFT:\n", F)
print("\nTask 2a - IDFT:\n", fa)
print("\nTask 2b - IDFT:\n", fb)
Task 1 - DFT:
[0.+0.j 2.-2.j 0.+0.j 2.+2.j]
Task 2a - IDFT:
[ 1.+0.j 1.+0.j -1.+0.j -1.+0.j]
Task 2b - IDFT:
[8.25+0.j 7.75+0.j 7.25+0.j 6.75+0.j]
LAB 6
# LAB 6
import numpy as np
import matplotlib.pyplot as plt
import sympy as sp
from sympy import symbols, summation, oo, exp, lambdify
# Define the symbolic variables
n, z = symbols('n z')
# Function 1: f(n) = 3^n
def z_transform_1():
f = 3**n
Z = summation(f * z**(-n), (n, 0, oo))
return Z.simplify()
# Function 2: f(n) = (-0.5)^n
def z_transform_2():
f = (-0.5)**n
Z = summation(f * z**(-n), (n, 0, oo))
return Z.simplify()
# Function 3: f(n) = n^2
def z_transform_3():
f = n**2
Z = summation(f * z**(-n), (n, 0, oo))
return Z.simplify()
# Calculate and print the Z-transforms
print("Z-transform of f(n) = 3^n:")
print(z_transform_1())
print("\nZ-transform of f(n) = (-0.5)^n:")
print(z_transform_2())
print("\nZ-transform of f(n) = n^2:")
print(z_transform_3())
# Visualization of the functions and their poles/zeros
def plot_z_transform(expr, title):
# Convert symbolic expression to numerical function
num_func = lambdify(n, expr, 'numpy')
# Plot the discrete-time function
n_vals = np.arange(0, 10)
f_vals = num_func(n_vals)
plt.figure(figsize=(10, 4))
plt.subplot(1, 2, 1)
plt.stem(n_vals, f_vals)
plt.title(f'Discrete Function: {title}')
plt.xlabel('n')
plt.ylabel('f(n)')
plt.grid(True, alpha=0.3)
# For pole-zero plot (simplified visualization)
plt.subplot(1, 2, 2)
if title == "3^n":
plt.plot(3, 0, 'rx', markersize=10) # Pole at z=3
elif title == "(-0.5)^n":
plt.plot(-0.5, 0, 'rx', markersize=10) # Pole at z=-0.5
elif title == "n^2":
plt.plot(1, 0, 'rx', markersize=10) # Pole at z=1 (double
pole)
plt.title('Pole Location (simplified)')
plt.xlabel('Real')
plt.ylabel('Imaginary')
plt.grid(True)
plt.axis('equal')
plt.xlim(-4, 4)
plt.ylim(-2, 2)
plt.tight_layout()
plt.show()
# Plot each function
plot_z_transform(3**n, "3^n")
plot_z_transform((-0.5)**n, "(-0.5)^n")
plot_z_transform(n**2, "n^2")
Z-transform of f(n) = 3^n:
Piecewise((z/(z - 3), (z > 3) | (z < -3)), (Sum(3**n/z**n, (n, 0,
oo)), True))
Z-transform of f(n) = (-0.5)^n:
Sum((-0.5)**n/z**n, (n, 0, oo))
Z-transform of f(n) = n^2:
WARNING:matplotlib.axes._base:Ignoring fixed x limits to fulfill fixed
data aspect with adjustable data limits.
WARNING:matplotlib.axes._base:Ignoring fixed y limits to fulfill fixed
data aspect with adjustable data limits.
Piecewise((z*(z + 1)/(z - 1)**3, (z > 1) | (z < -1)), (Sum(n**2/z**n,
(n, 0, oo)), True))
WARNING:matplotlib.axes._base:Ignoring fixed x limits to fulfill fixed
data aspect with adjustable data limits.
WARNING:matplotlib.axes._base:Ignoring fixed y limits to fulfill fixed
data aspect with adjustable data limits.
WARNING:matplotlib.axes._base:Ignoring fixed x limits to fulfill fixed
data aspect with adjustable data limits.
WARNING:matplotlib.axes._base:Ignoring fixed y limits to fulfill fixed
data aspect with adjustable data limits.
LAB 7
import numpy as np
import matplotlib.pyplot as plt
from scipy import signal
# Set up ultra-compact plotting style
plt.style.use('default')
plt.rcParams['figure.figsize'] = (10, 8) # Single figure with
subplots
plt.rcParams['axes.grid'] = True
plt.rcParams['grid.alpha'] = 0.2
plt.rcParams['axes.titlepad'] = 4
plt.rcParams['axes.labelpad'] = 2
# Parameters for filters
fs = 1000
center_freq = 100
bandwidth = 40
# Design filters
bp_b, bp_a = signal.butter(5, [center_freq-bandwidth/2,
center_freq+bandwidth/2],
btype='bandpass', fs=fs)
bs_b, bs_a = signal.butter(5, [center_freq-bandwidth/2,
center_freq+bandwidth/2],
btype='bandstop', fs=fs)
# Create test signal
t = np.linspace(0, 1, fs, False)
input_signal = np.sin(2*np.pi*30*t) + np.sin(2*np.pi*100*t) +
np.sin(2*np.pi*200*t)
# Process signals
bp_filtered = signal.lfilter(bp_b, bp_a, input_signal)
bs_filtered = signal.lfilter(bs_b, bs_a, input_signal)
# Create a single figure with all plots
fig, axs = plt.subplots(3, 2, constrained_layout=True)
# Frequency Responses
w, h_bp = signal.freqz(bp_b, bp_a, fs=fs)
axs[0,0].plot(w, 20*np.log10(abs(h_bp)), linewidth=1)
axs[0,0].set_title('Band Pass Frequency Response', fontsize=9)
axs[0,0].set_ylim(-60, 5)
w, h_bs = signal.freqz(bs_b, bs_a, fs=fs)
axs[0,1].plot(w, 20*np.log10(abs(h_bs)), linewidth=1)
axs[0,1].set_title('Band Stop Frequency Response', fontsize=9)
axs[0,1].set_ylim(-60, 5)
# Time Domain Signals
axs[1,0].plot(t[:100], input_signal[:100], 'b-', linewidth=1,
label='Original')
axs[1,0].plot(t[:100], bp_filtered[:100], 'r-', linewidth=1,
label='Filtered')
axs[1,0].set_title('Band Pass: Time Domain', fontsize=9)
axs[1,0].legend(fontsize=7)
axs[1,1].plot(t[:100], input_signal[:100], 'b-', linewidth=1,
label='Original')
axs[1,1].plot(t[:100], bs_filtered[:100], 'r-', linewidth=1,
label='Filtered')
axs[1,1].set_title('Band Stop: Time Domain', fontsize=9)
axs[1,1].legend(fontsize=7)
# Pole-Zero Plots
def plot_pz(ax, b, a, title):
z, p, k = signal.tf2zpk(b, a)
ax.scatter(np.real(z), np.imag(z), marker='o', facecolors='none',
edgecolors='b', s=40)
ax.scatter(np.real(p), np.imag(p), marker='x', color='r', s=40)
ax.axhline(0, color='black', linewidth=0.5)
ax.axvline(0, color='black', linewidth=0.5)
ax.set_title(title, fontsize=9)
ax.set_xlim(-1.5, 1.5)
ax.set_ylim(-1.5, 1.5)
plot_pz(axs[2,0], bp_b, bp_a, 'Band Pass: Pole-Zero')
plot_pz(axs[2,1], bs_b, bs_a, 'Band Stop: Pole-Zero')
# Common formatting
for ax in axs.flat:
ax.tick_params(axis='both', which='major', labelsize=7)
ax.grid(True, alpha=0.2)
plt.suptitle('Filter Analysis: Band Pass vs Band Stop', y=1.02,
fontsize=10)
plt.show()
LAB 8
import numpy as np
import matplotlib.pyplot as plt
from scipy import signal
# Set up compact plotting style
plt.rcParams['figure.figsize'] = (8, 4)
plt.rcParams['axes.grid'] = True
def plot_filters():
# Common parameters
cutoff = 50 # Cutoff frequency (rad/s)
order = 4
# Create frequency range
w = np.logspace(-1, 2, 1000)
# 1. Butterworth Filter
b_butter, a_butter = signal.butter(order, cutoff, 'low',
analog=True)
w_butter, h_butter = signal.freqs(b_butter, a_butter, worN=w)
# 2. Chebyshev Type I with different rp values
rp_values = [1, 3, 5] # Passband ripple in dB
cheby1_results = []
for rp in rp_values:
b, a = signal.cheby1(order, rp, cutoff, 'low', analog=True)
w_cheby1, h_cheby1 = signal.freqs(b, a, worN=w)
cheby1_results.append((rp, w_cheby1, h_cheby1))
# 3. Chebyshev Type II with different rs values
rs_values = [20, 40, 60] # Stopband attenuation in dB
cheby2_results = []
for rs in rs_values:
b, a = signal.cheby2(order, rs, cutoff, 'low', analog=True)
w_cheby2, h_cheby2 = signal.freqs(b, a, worN=w)
cheby2_results.append((rs, w_cheby2, h_cheby2))
# Plot results
fig, (ax1, ax2, ax3) = plt.subplots(1, 3, sharey=True)
# Butterworth plot
ax1.semilogx(w_butter, 20*np.log10(abs(h_butter)))
ax1.set_title('Butterworth (N=4)')
ax1.set_xlabel('Frequency [rad/s]')
ax1.set_ylabel('Amplitude [dB]')
ax1.axvline(cutoff, color='green', linestyle='--')
ax1.set_ylim(-60, 5)
# Chebyshev Type I plot
for rp, w_cheby1, h_cheby1 in cheby1_results:
ax2.semilogx(w_cheby1, 20*np.log10(abs(h_cheby1)),
label=f'rp={rp}dB')
ax2.set_title('Chebyshev Type I (N=4)')
ax2.set_xlabel('Frequency [rad/s]')
ax2.axvline(cutoff, color='green', linestyle='--')
ax2.legend()
# Chebyshev Type II plot
for rs, w_cheby2, h_cheby2 in cheby2_results:
ax3.semilogx(w_cheby2, 20*np.log10(abs(h_cheby2)),
label=f'rs={rs}dB')
ax3.set_title('Chebyshev Type II (N=4)')
ax3.set_xlabel('Frequency [rad/s]')
ax3.axvline(cutoff, color='green', linestyle='--')
ax3.legend()
plt.tight_layout()
plt.show()
plot_filters()