# -*- coding: utf-8 -*-
"""
Created on Sat Mar 15 21:42:13 2025

@author: AKourgli
"""

import numpy as np
import matplotlib.pyplot as plt
from scipy.signal import firwin, lfilter

# Paramètres de simulation
n_symbols = 100       # Augmenté pour une meilleure statistique BER
sps = 16
T = 1
fs = sps/T
Rs = 1/T
B_Nyquist = Rs/2
B_canal = 0.5         # Canal non-Nyquist
EbN0_dB = 10           # Rapport signal/bruit en dB

# Génération des bits et modulation BPSK
bits = np.random.randint(0, 2, n_symbols)
symboles = 2*bits - 1

# Suréchantillonnage et mise en forme
signal_sur = np.zeros(n_symbols * sps)
signal_sur[::sps] = symboles
pulse = np.ones(sps)
signal_emis = np.convolve(signal_sur, pulse, 'same')

# Filtre canal
numtaps = 101
fc_norm = B_canal/(fs/2)
coeffs = firwin(numtaps, fc_norm, window='hamming')

# Application du canal avec bruit
signal_recu_filtre = lfilter(coeffs, 1.0, signal_emis)

# Calcul du bruit
Eb = sps  # Energie par bit
EbN0_lin = 10 ** (EbN0_dB / 10)
sigma = np.sqrt(Eb / (2 * EbN0_lin))
noise = sigma * np.random.randn(len(signal_recu_filtre))
signal_recu = signal_recu_filtre + noise

# Compensation retard
retard = (numtaps - 1)//2
signal_recu_filtre = signal_recu_filtre[retard:]
signal_recu = signal_recu[retard:]
signal_emis = signal_emis[:len(signal_recu)]

# Échantillonnage
n_available = len(signal_recu) // sps
echantillons = signal_recu[:n_available*sps:sps]
bits_recus = (np.sign(echantillons) + 1) // 2

# Calcul BER
ber = np.mean(bits[:n_available] != bits_recus)

# Affichage des résultats
plt.figure(figsize=(12, 8))

# Signal avec bruit
plt.subplot(311)
plt.plot(signal_emis, 'b', label='Signal émis')
plt.plot(signal_recu_filtre, 'r', alpha=0.7, label='Signal reçu')
plt.title('Signaux émis et reçu')
plt.xlabel('Temps (échantillons)')
plt.ylabel('Amplitude')
plt.legend()
plt.grid(True)

plt.subplot(312)
plt.plot(signal_recu, 'r', alpha=0.7)
plt.title(f'Signal reçu avec bruit (Eb/N0={EbN0_dB} dB)')
plt.xlabel('Temps (échantillons)')
plt.ylabel('Amplitude')
plt.grid(True)

# Comparaison des bits
plt.subplot(313)
plt.stem(bits[:100], linefmt='b-', markerfmt='bo', label='Bits émis')
plt.stem(bits_recus[:100], linefmt='r--', markerfmt='rx', label='Bits reçus')
plt.title(f'Comparaison des premiers 100 bits (BER={ber:.4f})')
plt.xlabel('Index bit')
plt.ylabel('Valeur')
plt.legend()
plt.grid(True)


# Diagramme de l'œil
plt.figure()
duree_oeil = 2*sps
n_oeil = len(signal_recu) // duree_oeil
samples_oeil = signal_recu[:n_oeil*duree_oeil].reshape(-1, duree_oeil)
for i in range(min(50, n_oeil)):
    plt.plot(samples_oeil[i,:], 'b-', alpha=0.5)
plt.title('Diagramme de l\'œil dégradé')
plt.grid(True)
plt.tight_layout()
plt.show()