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

  1. Set up the circuit on a breadboard and switch on the power supply and signal generators.
  2. Feed the sine wave and clock from the signal generator.
  3. 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

p><br><p><img src=

2Q==

2Q==

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

  1. Let the circuit and observe the free-running frequency f0.
  2. Feed a square pulse input signal and its frequency from 100 Hz to 1 MHz and note down fc1 and fL2.
  3. Decrease the frequency from a high value to a low value and note down fc2 and fL1.
  4. 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

p><p><img src=

Cgf8HnHfQWkMXY7MAAAAASUVORK5CYII=

wMogAM7TLO84wAAAABJRU5ErkJggg==