Digital Modulation Techniques: BPSK, QPSK, Pulse Shaping, and Eye Diagrams
BPSK
Python Implementation of BPSK Modulation
import numpy as np
import matplotlib.pyplot as plt
# Parameters
message_frequency = 10
carrier_frequency = 20
sampling_frequency = 30 * carrier_frequency
duration = 4 / carrier_frequency
# Time vector
t = np.arange(0, duration, 1 / sampling_frequency)
# Message signal
message = np.sign(np.cos(2 * np.pi * message_frequency * t) + np.random.normal(scale=0.01, size=len(t)))
# Carrier signal
carrier = np.cos(2 * np.pi * carrier_frequency * t)
# BPSK modulated signal
modulated_signal = carrier * message
# Plotting
plt.figure(figsize=(10, 8))
plt.subplot(3, 1, 1)
plt.plot(t, message)
plt.title(‘Message Signal’)
plt.xlabel(‘Time [s]’)
plt.ylabel(‘Amplitude’)
plt.subplot(3, 1, 2)
plt.plot(t, carrier)
plt.title(‘Carrier Signal’)
plt.xlabel(‘Time [s]’)
plt.ylabel(‘Amplitude’)
plt.subplot(3, 1, 3)
plt.plot(t, modulated_signal)
plt.title(‘BPSK Modulated Signal’)
plt.xlabel(‘Time [s]’)
plt.ylabel(‘Amplitude’)
plt.tight_layout()
plt.show()
# Overlay plot
plt.figure(figsize=(10, 6))
plt.plot(t, message, label=’Message Signal’)
plt.plot(t, carrier, label=’Carrier Signal’)
plt.plot(t, modulated_signal, ‘–‘, label=’BPSK Modulated Signal’)
plt.title(‘Overlay of Message, Carrier, and BPSK Modulated Signals’)
plt.xlabel(‘Time [s]’)
plt.ylabel(‘Amplitude’)
plt.legend()
plt.show()
BER in BPSK
import numpy as np
import matplotlib.pyplot as plt
# Parameters
N = 500000
EbN0dB_list = np.arange(0, 15)
BER = []
# Monte Carlo Simulation
for i in range(len(EbN0dB_list)):
EbN0dB = EbN0dB_list[i]
EbN0 = 10**(EbN0dB/10)
x = 2 * (np.random.rand(N) >= 0.5) – 1
noise = 1 / np.sqrt(2 * EbN0)
channel = x + np.random.randn(N) * noise
received_x = 2 * (channel >= 0.5) – 1
errors = (x != received_x).sum()
BER.append(errors / N)
# Plotting
plt.figure(figsize=(10, 6))
plt.plot(EbN0dB_list, BER, “-“, label=’BER’)
plt.plot(EbN0dB_list, BER, “go”)
plt.axis([0, 14, 1e-7, 0.1])
plt.xscale(‘linear’)
plt.yscale(‘log’)
plt.grid(True)
plt.xlabel(“Eb/N0 in dB”)
plt.ylabel(“BER”)
plt.title(“BER in BPSK”)
plt.legend()
plt.show()
QPSK
Python Implementation of QPSK Modulation
import numpy as np
import matplotlib.pyplot as plt
fm = 10, fc = 30, overSamplingRate = 20, fs = overSamplingRate * fc
def cosineWave(f, overSamplingRate, nCycles, phase):
fs = overSamplingRate * f
t = np.arange(0, nCycles*1/f, 1/fs)
g = np.cos(2 * np.pi * f * t + phase)
return list(g)
# Random binary message generation
x = np.random.rand(30) >= 0.5
str_x = [str(int(i)) for i in x]
x = “”.join(str_x)
print(“Message string: {}”.format(x))
# Group message into 2-bit combinations
message = [x[2*i:2*(i+1)] for i in range(int(len(x)/2))]
print(“Message string grouped as combinations of 2 bits each: {}”.format(message))
# Generate QPSK modulated signals for different bit combinations
pi = np.pi
mod_00 = cosineWave(fc, overSamplingRate, fc/fm, 3*pi/4)
mod_01 = cosineWave(fc, overSamplingRate, fc/fm, pi/4)
mod_10 = cosineWave(fc, overSamplingRate, fc/fm, -3*pi/4)
mod_11 = cosineWave(fc, overSamplingRate, fc/fm, -pi/4)
# Modulate the signal
modulated_signal = []
for i in message:
if i == ’00’:modulated_signal += mod_00
elif i == ’01’:modulated_signal += mod_01
elif i == ’10’:modulated_signal += mod_10
elif i == ’11’:modulated_signal += mod_11
# Time vector
t = np.arange(0, (len(x)/2) * 1/fm, 1/fs)
print(len(t), len(modulated_signal))
# Plot modulated signal
plt.figure(figsize=(28, 6))
plt.plot(t, modulated_signal)
plt.xlabel(“Time”)
plt.ylabel(“Amplitude”)
plt.title(“Modulated signal”)
plt.grid(True)
plt.show()
BER in QPSK
# Error performance of QPSK
N = 500000
EbN0dB_list = np.arange(0, 15)
BER = []
for i in range(len(EbN0dB_list)):
EbN0dB = EbN0dB_list[i]
EbN0 = 10**(EbN0dB/10)
x = np.random.rand(N) >= 0.5
x_str = [str(int(i)) for i in x]
x_str = “”.join(x_str)
message = [x_str[2*j:2*(j+1)] for j in range(int(len(x)/2))]
noise = 1/np.sqrt(2 * EbN0)
channel = x + np.random.randn(N) * noise
received_x = channel >= 0.5
xReceived_str = [str(int(i)) for i in received_x]
xReceived_str = “”.join(xReceived_str)
messageReceived = [xReceived_str[2*j:2*(j+1)] for j in range(int(len(x)/2))]
message = np.array(message)
messageReceived = np.array(messageReceived)
errors = (message != messageReceived).sum()
BER.append(errors/N)
print(BER)
plt.figure(figsize=(10, 6))
plt.plot(EbN0dB_list, BER, “-“, label=’BER’)
plt.plot(EbN0dB_list, BER, “go”)
plt.xscale(‘linear’)
plt.yscale(‘log’)
plt.grid(True)
plt.xlabel(“Eb/N0 (dB)”)
plt.ylabel(“BER”)
plt.title(“BER in QPSK”)
plt.legend()
plt.show()
Pulse Shaping and Matched Filters
Python Implementation of Pulse Shaping
import numpy as np
import matplotlib.pyplot as plt
from scipy import signal
# Parameters
num_symbols = 10
sps = 8 # Samples per symbol
num_taps = 101 # Number of filter taps
beta = 0.35 # Roll-off factor for the raised cosine filter
# Generate random bits
bits = np.random.randint(0, 2, num_symbols) # Our data to be transmitted, 1’s and 0’s
# Create the baseband signal with pulse shaping
x = np.array([])
for bit in bits:
pulse = np.zeros(sps)
pulse[0] = bit * 2 – 1 # set the first value to either a 1 or -1
x = np.concatenate((x, pulse)) # add the 8 samples to the signal
# Plot the baseband signal
plt.figure(0)
plt.plot(x, ‘.-‘)
plt.title(‘Baseband Signal’)
plt.grid(True)
plt.show()
# Create our raised-cosine filter
Ts = sps # Symbol period in samples
t = np.arange(-num_taps // 2, num_taps // 2 + 1) # Time vector for filter
h = (1 / Ts) * np.sinc(t / Ts) * np.cos(np.pi * beta * t / Ts) / (1 – (2 * beta * t / Ts) ** 2)
h[np.isnan(h)] = np.pi / 4 # Handle division by zero
# Plot the raised-cosine filter
plt.figure(1)
plt.plot(t, h, ‘.-‘)
plt.title(‘Raised-Cosine Filter’)
plt.grid(True)
plt.show()
# Apply the filter to the baseband signal
x_shaped = np.convolve(x, h)
# Plot the filtered signal
plt.figure(2)
plt.plot(x_shaped, ‘.-‘)
for i in range(num_symbols):
plt.plot([i * sps + num_taps // 2, i * sps + num_taps // 2], [0, x_shaped[i * sps + num_taps // 2]], ‘r’)
plt.title(‘Filtered Signal’)
plt.grid(True)
plt.show()
Eye diagram
Python Implementation for Generating Eye Diagrams
import numpy as np
import matplotlib.pyplot as plt
import warnings
def get_filter(name, T, rolloff=None):
def rc(t, beta):
with warnings.catch_warnings():
warnings.simplefilter(“ignore”)
return np.sinc(t) * np.cos(np.pi * beta * t) / (1 – (2 * beta * t) ** 2)
def rrc(t, beta):
return (np.sin(np.pi * t * (1 – beta)) + 4 * beta * t * np.cos(np.pi * t * (1 + beta))) / (np.pi * t * (1 – (4 * beta * t) ** 2))
# rolloff is ignored for triang and rect
if name == ‘rect’:
return lambda t: (abs(t / T) <= 0.5).astype(float)
elif name == ‘triang’:
return lambda t: (1 – abs(t / T)) * (abs(t / T) <= 1).astype(float)
elif name == ‘rc’:
return lambda t: rc(t / T, rolloff)
elif name == ‘rrc’:
return lambda t: rrc(t / T, rolloff)
# Plot the pulses
T = 1
Fs = 100
t = np.arange(-3 * T, 3 * T, 1 / Fs)
plt.figure(figsize=(8, 3))
plt.plot(t, get_filter(‘rc’, T, rolloff=0.5)(t), label=r’Raised cosine alpha=0.5′)
plt.plot(t, get_filter(‘rrc’, T, rolloff=0.5)(t), label=r’Root raised cosine alpha=0.5′)
plt.plot(t, get_filter(‘rect’, T)(t), label=r’Rectangular’)
plt.plot(t, get_filter(‘triang’, T)(t), label=r’Triangular’, lw=2)
plt.legend()
plt.grid(True)
plt.show()
def get_signal(g, d):
“””Generate the transmit signal as sum(d[k]*g(t-kT))”””
t = np.arange(-2 * T, (len(d) + 2) * T, 1 / Fs)
g0 = g(np.array([1e-8]))
xt = sum(d[k] * g(t – k * T) for k in range(len(d)))
return t, xt / g0
# Generate the signal
binary_sequence = [0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0]
g = get_filter(“rrc”, T, 1)
t, y = get_signal(g, np.array(binary_sequence) * 2 – 1)
# Plot the results
fig, ax = plt.subplots(2, 2, figsize=(12, 8))
# Binary message
ax[0][0].step(np.arange(len(binary_sequence)), binary_sequence)
ax[0][0].set_title(“Binary message”)
# BPSK Signal
ax[0][1].plot(t, y)
ax[0][1].set_title(“BPSK Signal”)
# Eye diagram
x = np.arange(-T, T, 1 / Fs)
for i in range(2 * Fs, len(y) – 3 * Fs, Fs):
ax[1][1].plot(x, y[i:i + 2 * Fs], ‘blue’)
ax[1][1].set_title(“Eye diagram”)
# Pulse shape
t_pulse = np.arange(-5, 5, 0.01)
ax[1][0].plot(t_pulse, g(t_pulse))
ax[1][0].set_title(“Pulse shape”)
plt.tight_layout()
plt.show()
Performance of Waveform Coding Using PCM
Python Implementation of PCM
import numpy as np
import matplotlib.pyplot as plt
# Message signal generation
fm = 100
dc_offset = 2
t = np.arange(0, 5/fm, 0.0001)
x = np.sin(2 * np.pi * fm * t) + dc_offset
plt.figure(figsize=(8, 6))
plt.plot(t, x)
plt.xlabel(“Time”)
plt.ylabel(“Amplitude”)
plt.title(“Message Signal”)
plt.grid(True)
plt.show()
# Sampling
fs = 30 * fm
t_sampled = np.arange(0, 5/fm, 1/fs)
x_sampled = np.sin(2 * np.pi * fm * t_sampled) + dc_offset
plt.figure(figsize=(8, 6))
plt.plot(t_sampled, x_sampled, “bo-“)
plt.xlabel(“Time”)
plt.ylabel(“Amplitude”)
plt.title(“Sampled Message Signal”)
plt.grid(True)
plt.show()
# Quantization
L = 8
x_min = min(x)
x_max = max(x)
quantization_levels = np.linspace(x_min, x_max, L)
x_quantized = []
q_input = np.linspace(x_min, x_max, 1000)
q_output = []
for i in q_input:
for j in quantization_levels:
if i <= j:
q_output.append(j)
break
for i in x_sampled:
for j in quantization_levels:
if i <= j:
x_quantized.append(j)
break
plt.figure(figsize=(12, 6))
plt.subplot(1, 2, 1)
plt.plot(q_input, q_output, “r-“)
plt.xlabel(“Input”)
plt.ylabel(“Output”)
plt.title(“Quantizer Characteristics L=8”)
plt.grid(True)
plt.subplot(1, 2, 2)
plt.plot(t_sampled, x_quantized, “bo-“)
plt.xlabel(“Time”)
plt.ylabel(“Amplitude”)
plt.title(“Sampled and Quantized Message Signal”)
plt.grid(True)
plt.show()
# Encoding
qlevels = {}
bit_no = int(np.log2(L))
for i in range(L):
val = bin(i).replace(‘0b’, “”)
if len(val) != bit_no:
bin_str = “0” * (bit_no – len(val)) + val
else:
bin_str = val
qlevels[quantization_levels[i]] = bin_str
x_encoded = [qlevels[i] for i in x_quantized]
print(“Pulse coded message : {}”.format(x_encoded))
# Signal to Noise Ratio (SNR)
def power(lst):
P = 0
for i in lst:
P += i ** 2
return P / len(lst)
quantization_noise = np.array(x_quantized) – np.array(x_sampled)
plt.plot(t_sampled, quantization_noise)
plt.xlabel(“Time”)
plt.ylabel(“Noise”)
plt.title(“Quantization Noise”)
plt.grid(True)
plt.show()
snr = power(x_sampled) / power(quantization_noise)
snrdB = 20 * np.log10(snr)
step_size = (x_max – x_min) / L
noise_power = (step_size ** 2) / 3
snr_eqn = power(x_sampled) / noise_power
snr_eqn_dB = 20 * np.log10(snr_eqn)
print(“SNR : {:.2f} dB”.format(snrdB))
print(“SNR from equation : {:.2f} dB”.format(snr_eqn_dB))
BPSK Generation and Detection
Aim
To design and set up a Binary Phase Shift Keying (BPSK) generator.
Components and Equipments Required
- Analog switch CD4016
- IC 741
- IC 7404
- Signal generator
- Resistor
- Power supply
- Breadboard
- CRO etc.
Theory
In the BPSK modulation system, the phase of the carrier wave is inverted according to the logic level of the input data. When the data is at logic one level, the sinusoid has one fixed phase and when the data is at the other level, the phase of the sinusoid changes. BPSK and BFSK signals have a constant envelope and hence they are less susceptible to noise. Two switches inside the quad analog switch CD4016 are used in the circuit. An Op-amp is used to invert the phase of the input sine wave.
Procedure
- Set up the circuit on a breadboard and switch on the power supply and signal generators.
- Feed the sine wave and clock from the signal generator.
- Keep the clock frequency lower than the sine wave frequency and observe the output.
Design
Gain of inverting amplifier, A=-Rf/R1
Let the gain be -1, so that the ratio Rf/R1=1. Take R1=Rf=4.7K
Study of CMOS PLL
Aim
To familiarize with CMOS PLL IC CD4046A and study its functional characteristics.
Components Required
- PLL IC
- Capacitors
- Oscilloscope
- Function Generator
- Resistors
- Bread board
- DC Power supply
- Connector wires
Theory
If a voice or music (i.e., modulating signal) is applied to the VCO instead of digital data, the oscillator’s frequency will move or modulate with the voice or music, this is frequency modulation “FM”. It’s simply moving the frequency in relation to some input voltage which also represents a voltage to frequency conversion.
PLL is a circuit designed to synchronize with an incoming signal and remain in synchronization despite the incoming signal variations. PLL mainly consists of the phase detector, a LPF, and a VCO. The phase detector provides a DC Voltage proportional to the difference between the inputs. LPF removes high-frequency noise. The DC voltage controls the VCO frequency which is then fed back and compared with input frequency and automatically gets itself equal to input frequency.
IC 4046-A is a silicon gate CMOS device that has pins compatible with 4046-B. It consists of a VCO and a three-phase gate. Large signals can be connected directly to the signal inputs of the PLL while a series capacitor is used to couple the signal inputs to connect the small signals.
Design
Centre frequency is given by fo = 1/2R1(C1+32PF) at VDD 5V
Let the required fo= 10 kHz. Let R1= 100kHz.
Then C1= 500 pF use 470 pF.
Capture range 2fc= (1/ π) *(2 πf2/R3C2)1/2
Let the captured range be 800 Hz. Take R3= 100kΩ. Then C2= 0.1 μF
Procedure
- Let the circuit and observe the free-running frequency f0.
- Feed a square pulse input signal and its frequency from 100 Hz to 1 MHz and note down fc1 and fL2.
- Decrease the frequency from a high value to a low value and note down fc2 and fL1.
- Calculate the capture range fc = fc2 – fc1 and lock range fL= fL2 – fL1.
Result
- Familiarized with CMOS PLL IC 4046A and studied its functional characteristics.
- Free running frequency, f0 = 11.1 KHz
- Lock range = 12.5 KHz
- Capture range = 4.3 KH
