# -*- coding: utf-8 -*-
"""
Created on Fri Apr 11 12:05:04 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)
    
    if M == 2:  # BPSK
        return Q(np.sqrt(2 * EbN0))
 
    else:  # M-PSK (M > 4)
        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 à tester
modulations = {
    "M-ASK": ber_m_ask,
    "M-PSK": ber_m_psk,
    "M-QAM": ber_m_qam,
    "M-FSK": ber_m_fsk,
}
markers = ['o', 's', '^', 'D', 'v', 'p','P']  # Marqueurs pour chaque M

# ================================================================
# Tracé des courbes pour chaque modulation
# ================================================================
for mod_name, ber_func in modulations.items():
    plt.figure(figsize=(10, 6))
    for idx, M in enumerate(M_values):
        # Calcul du BER
        ber = ber_func(EbN0_dB, M)
        # Tracé avec marqueur unique par M
        plt.semilogy(
            EbN0_dB, ber, 
            marker=markers[idx], 
            linestyle='-', 
            linewidth=2, 
            markersize=6, 
            label=f'M = {M}'
        )
    
    # Mise en forme du graphique
    plt.xlabel("Eb/N0 (dB)", fontsize=12)
    plt.ylabel("BER", fontsize=12)
    plt.title(f"BER théorique pour {mod_name} (AWGN)", fontsize=14)
    plt.grid(True, which="both", linestyle="--", alpha=0.6)
    plt.legend(title="Ordre M", fontsize=10)
    plt.ylim(1e-8, 1)
    plt.xlim(0, 40)
    plt.tight_layout()
    plt.show()