0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

QAMのpython実装

Posted at
AI_qammod_demod.py

import numpy as np
import matplotlib.pyplot as plt

# =====================================
# Multi-QAM (16, 32, 64) Modem Functions
# =====================================

def bits_to_gray(n):
    """整数をGrayコードに変換"""
    return n ^ (n >> 1)

def gray_levels(n_bits):
    """Gray符号化されたPAMレベルを生成"""
    M = 2 ** n_bits
    levels = np.arange(-(M - 1), M, 2)
    bin_to_level = np.zeros(M, dtype=float)
    for i in range(M):
        gray_i = bits_to_gray(i)
        bin_to_level[i] = levels[gray_i]
    return bin_to_level

def make_qam_constellation(M, normalize=True):
    """
    任意M-QAMコンステレーション(Gray符号化)を生成
    対応:M = 16, 32, 64
    """
    if M == 16:
        n_I, n_Q = 2, 2  # 4x4
    elif M == 32:
        n_I, n_Q = 3, 2  # 8x4 (近似的な正方格子)
    elif M == 64:
        n_I, n_Q = 3, 3  # 8x8
    else:
        raise ValueError("M must be one of 16, 32, 64")

    I_levels = gray_levels(n_I)
    Q_levels = gray_levels(n_Q)
    const_points = []
    bit_labels = []

    for i in range(2**n_I):
        for q in range(2**n_Q):
            sym = I_levels[i] + 1j * Q_levels[q]
            bits_i = [int(b) for b in format(i, f'0{n_I}b')]
            bits_q = [int(b) for b in format(q, f'0{n_Q}b')]
            bit_labels.append(bits_i + bits_q)
            const_points.append(sym)

    const_points = np.array(const_points)
    bit_labels = np.array(bit_labels)

    # 正規化(平均電力 = 1)
    if normalize:
        Es = np.mean(np.abs(const_points)**2)
        const_points /= np.sqrt(Es)

    # ビット列→シンボル辞書を作成
    mapping = {tuple(bits.tolist()): sym for bits, sym in zip(bit_labels, const_points)}

    return const_points, bit_labels, mapping, n_I + n_Q

# ----------------------------------
# 変調・復調・BER・チャネル関数群
# ----------------------------------

def qam_modulate(bits, M):
    """ビット列をM-QAMシンボルに変換"""
    const_points, bit_labels, mapping, k = make_qam_constellation(M)
    bits = np.asarray(bits).astype(int)
    assert bits.size % k == 0, f"Bits length must be multiple of {k} for {M}-QAM"
    n_sym = bits.size // k
    syms = np.zeros(n_sym, dtype=complex)
    for i in range(n_sym):
        btuple = tuple(bits[i*k:(i+1)*k].tolist())
        syms[i] = mapping[btuple]
    return syms, const_points, bit_labels, k

def qam_demodulate(symbols, const_points, bit_labels):
    """M-QAMをハード判定復調"""
    dists = np.abs(symbols.reshape(-1, 1) - const_points.reshape(1, -1))
    idx = np.argmin(dists, axis=1)
    bits_out = bit_labels[idx].reshape(-1)
    return bits_out.astype(int)

def awgn_channel(symbols, EbN0_dB, k):
    """AWGNチャネル"""
    EbN0 = 10**(EbN0_dB/10)
    N0 = 1.0 / (EbN0 * k)
    sigma2 = N0/2.0
    noise = np.sqrt(sigma2) * (np.random.randn(len(symbols)) + 1j*np.random.randn(len(symbols)))
    return symbols + noise

def ber(bits_tx, bits_rx):
    """BER計算"""
    bits_tx = np.asarray(bits_tx).astype(int)
    bits_rx = np.asarray(bits_rx).astype(int)
    assert bits_tx.shape == bits_rx.shape
    errors = np.sum(bits_tx != bits_rx)
    return errors / bits_tx.size, errors

def plot_constellation(const_points, rx_symbols=None, title="QAM Constellation"):
    """コンステレーション描画"""
    plt.figure(figsize=(6,6))
    plt.scatter(const_points.real, const_points.imag, marker='o', label='Ideal Points')
    if rx_symbols is not None:
        plt.scatter(rx_symbols.real, rx_symbols.imag, marker='.', s=10, alpha=0.3, label='Received')
    plt.axhline(0, color='gray', linewidth=0.5)
    plt.axvline(0, color='gray', linewidth=0.5)
    plt.xlabel("In-phase")
    plt.ylabel("Quadrature")
    plt.title(title)
    plt.legend()
    plt.grid(True)
    plt.gca().set_aspect('equal', adjustable='box')
    plt.show()

# -------------------------------
# 実行例(16, 32, 64-QAMテスト)
# -------------------------------
if __name__ == "__main__":
    for M in [16, 32, 64]:
        print(f"\n=== {M}-QAM Simulation ===")

        n_bits = 300000
        EbN0_dB = 10.0
        bits_tx = np.random.randint(0, 2, n_bits)

        # 変調
        symbols, const_points, bit_labels, k = qam_modulate(bits_tx, M)
        # チャネル
        rx = awgn_channel(symbols, EbN0_dB=EbN0_dB, k=k)
        # 復調
        bits_rx = qam_demodulate(rx, const_points, bit_labels)
        # BER
        ber_val, errors = ber(bits_tx, bits_rx)

        print(f"Eb/N0 = {EbN0_dB:.1f} dB, Bits = {n_bits}, Errors = {errors}, BER = {ber_val:.5e}")
        plot_constellation(const_points, rx_symbols=rx[:2000], title=f"{M}-QAM Constellation (Eb/N0={EbN0_dB} dB)")

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?