# -*- coding: utf-8 -*-
"""
Created on Wed Mar  5 11:04:19 2025

@author: AKourgli
"""

import numpy as np
import matplotlib.pyplot as plt

# Paramètres de simulation
symbol_duration = 1.0       # Durée d'un symbole (T)
samples_per_symbol = 20     # Nombre d'échantillons par symbole
num_symbols = 100          # Nombre total de symboles
rolloff = 0.5               # Facteur de roll-off

# Génération des symboles
symbols = np.random.choice([-1, 1], size=num_symbols)

# Création du filtre RRC avec gestion des singularités
t = np.linspace(-3, 3, 6 * samples_per_symbol + 1)
t = np.delete(t, len(t)//2)  # Évite la division par zéro au centre

# Calcul de la réponse impulsionnelle
h = np.zeros_like(t)
denominator = 1 - (2 * rolloff * t / symbol_duration)**2

# Calcul différencié pour éviter les singularités
with np.errstate(divide='ignore', invalid='ignore'):
    h = np.sinc(t / symbol_duration) * np.cos(np.pi * rolloff * t / symbol_duration) / denominator
    
# Application de la règle de l'Hospital pour t = ±T/(4α)
singularity_mask = np.abs(denominator) < 1e-9
h[singularity_mask] = (rolloff / (2 * np.sqrt(symbol_duration)) * np.sinc(1 / (2 * rolloff)))

# Normalisation et gestion des NaN
h[np.isnan(h)] = 0  # Suppression des valeurs non calculées
h /= np.sqrt(np.sum(h**2))  # Normalisation d'énergie

# Génération du signal
upsampled = np.zeros(num_symbols * samples_per_symbol)
upsampled[::samples_per_symbol] = symbols
signal = np.convolve(upsampled, h, mode='same')

# Ajout de bruit réaliste
snr_db = 25
noise_power = 10**(-snr_db/10) * np.var(signal)
signal += np.random.normal(0, np.sqrt(noise_power), signal.shape)

# Configuration du diagramme de l'oeil
samples_per_eye = 3 * samples_per_symbol  # 3 périodes de symbole
t_eye = (np.arange(samples_per_eye) - samples_per_eye//2) / samples_per_symbol

# Extraction des segments synchronisés
offset = len(h) // 2  # Compensation du retard du filtre
segments = []
for i in range(offset, len(signal) - samples_per_eye, samples_per_symbol):
    segments.append(signal[i:i + samples_per_eye])

# Tracé du diagramme
plt.figure(figsize=(12, 6))
for seg in segments[-200:]:
    plt.plot(t_eye, seg, color='navy', alpha=0.5, linewidth=1)


# Configuration des axes
plt.title(f'Diagramme de l\'œil sur 2 périodes de symbole T = {symbol_duration} pour un SNR = {snr_db} ')
plt.xlabel('Temps (en multiple de T)')
plt.ylabel('Amplitude')
plt.xticks(np.arange(-1, 1.1, 0.25))
plt.xlim(-1, 1)
plt.grid(True, linestyle='--', alpha=1)
plt.axvline(0.52, color='red', linestyle=':', linewidth=0.8)

# # Ajout de marqueurs temporels
# for t_marker in [-1, -0.5, 0, 0.5, 1]:
#     plt.axvline(t_marker, color='gray', linestyle=':', alpha=0.3, linewidth=0.5)

# plt.show()
# plt.title('Diagramme de l\'œil normalisé')
# plt.xlabel('Temps (en multiple de T)')
# plt.ylabel('Amplitude')
# plt.xlim(-1, 1)
# plt.grid(True, which='both', linestyle='--', alpha=0.5)
# #plt.axvline(0, color='red', linestyle='--', linewidth=0.5)
# plt.show()