# -*- coding: utf-8 -*-
"""
Created on Fri Apr 11 12:21:55 2025

@author: AKourgli
"""

import numpy as np
import matplotlib.pyplot as plt
from scipy.special import erfc

# Définition de la fonction Q
Q = lambda x: 0.5 * erfc(x / np.sqrt(2))

# ================================================================
# Définitions des fonctions BER
# ================================================================
def ber_m_ask(EbN0_dB, M):
    k = np.log2(M)
    EbN0 = 10**(EbN0_dB / 10)
    return (2*(M-1)/(M*k)) * Q(np.sqrt((6 * k * EbN0)/(M**2 - 1)))

def ber_m_psk(EbN0_dB, M):
    k = np.log2(M)
    EbN0 = 10**(EbN0_dB / 10)
    return (2/k) * Q(np.sqrt(2*k*EbN0) * np.sin(np.pi/M))

def ber_m_qam(EbN0_dB, M):
    k = np.log2(M)
    EbN0 = 10**(EbN0_dB / 10)
    return (4/k)*(1 - 1/np.sqrt(M)) * Q(np.sqrt((3*k*EbN0)/(M-1)))

def ber_m_fsk(EbN0_dB, M):
    k = np.log2(M)
    EbN0 = 10**(EbN0_dB / 10)
    return 0.5*(M-1) * erfc(np.sqrt(k*EbN0/2))

# ================================================================
# Paramètres de simulation
# ================================================================
EbN0_dB = np.arange(0, 40, 1)  # De 0 à 20 dB
M_values = [2, 4, 8, 16, 32, 64, 128]   # Valeurs de M à comparer
modulations = {
    "ASK": ber_m_ask,
    "PSK": ber_m_psk,
    "QAM": ber_m_qam,
    "FSK": ber_m_fsk,
}
colors = ["blue", "red", "green", "yellow","orange", "purple"]
markers = ['o', 's', '^', 'D', 'v', 'p','P'] 

# ================================================================
# Tracé des graphes (un par M)
# ================================================================
for M in M_values:
    plt.figure(figsize=(10, 6))
    
    # Calcul et tracé pour chaque modulation
    for idx, (mod_name, ber_func) in enumerate(modulations.items()):
        # Gestion des cas particuliers (ex: M=2 pour QAM = BPSK)
        if (mod_name == "QAM" and M not in [4, 16, 64]) or (mod_name == "ASK" and M < 2):
            continue  # Ignore les M non valides pour QAM/ASK
        ber = ber_func(EbN0_dB, M)
        plt.semilogy(
            EbN0_dB, ber, 
            marker=markers[idx], 
            linestyle='-', 
            color=colors[idx], 
            linewidth=2, 
            markersize=8, 
            label=f'{mod_name}'
        )
    
    # Mise en forme
    plt.xlabel("Eb/N0 (dB)", fontsize=12)
    plt.ylabel("Bit Error Rate (BER)", fontsize=12)
    plt.title(f"Comparaison des BER pour M = {M}", fontsize=14)
    plt.grid(True, which="both", linestyle="--", alpha=0.6)
    plt.legend()
    plt.ylim(1e-8, 1)
    plt.xlim(0, 40)
    plt.tight_layout()
    plt.show()