Python - QAM
Table of Contents
Introduction
In order to structure the utility relating to constellation modulation, I wrote a class Quadrature_Amplitude_Modulation in Python.
Source code
import numpy as np
import matplotlib.pyplot as plt
class Quadrature_Amplitude_Modulation:
def __init__(self, mod_order: int) -> None:
"""! Constructor for quadrature amplitude modulation (QAM)
@param mod_order Modulation order, e.g., 4 for QPSK.
"""
self.mod_order = mod_order
self.bit_num_sym = int(np.log2(mod_order))
m = np.sqrt(mod_order)
v = np.linspace(1 - m, m - 1, int(m))
x, y = np.meshgrid(v, v)
self.const = np.reshape(x + 1j * y, -1) / np.sqrt((mod_order - 1) * 2 / 3)
self.name = "QPSK" if mod_order == 4 else f"{mod_order}QAM"
def calc_mi(self, sinr: float, sample_num: int=1000) -> float:
"""! Calculate the per-symbol mutual information, a.k.a. RBIR, for the specified SINR.
@param sinr SINR in dB
@param sample_num Number of random samples
@return Per-bit information
"""
n_pwr = np.power(10, -0.1 * sinr)
n = np.random.randn(sample_num) + 1j * np.random.randn(sample_num)
n *= np.sqrt(n_pwr / 2)
si = np.zeros((self.mod_order, sample_num))
for sym_idx in range(self.mod_order):
for sample_idx in range(sample_num):
x = np.power(np.abs(self.const - self.const[sym_idx] + n[sample_idx]), 2) - np.power(np.abs(n[sample_idx]), 2)
si[sym_idx][sample_idx] = np.sum(np.exp(-1 * x / n_pwr))
return self.bit_num_sym - np.log2(si).mean()
def visualize(self, ax: plt.Axes = None, **kwargs) -> None:
"""! Visualize the constellation
@param ax An axis object
@param kwargs Keyword argument for further customization
"""
if ax is None:
_, ax = plt.subplots()
ax.scatter(self.const.real, self.const.imag, **kwargs)
ax.set_xlabel('In-phase')
ax.set_ylabel('Quadrature')
ax.set_title(self.name)
ax.set_aspect('equal')
ax.grid(True)