# -*- coding: utf-8 -*-

#Nom du programme : Echantillonnage_signal_analogique

#Auteurs : David Delgove, Arnaud Raoux et la prépa agreg de Montrouge, modifié par ENS de Lyon promotion 2017-2018
#Adresse : Departement de physique de l'Ecole Normale Superieure
#		24 rue Lhomond
#		75005 Paris
#Contact : arnaud.raoux@ens.fr

#Version de Python
#3.5

#LICENCE
#Cette oeuvre, création, site ou texte est sous licence Creative Commons Attribution - Pas d'Utilisation Commerciale 4.0 International. Pour accéder à une copie de cette licence, merci de vous rendre à l'adresse suivante http://creativecommons.org/licenses/by-nc/4.0/ ou envoyez un courrier à Creative Commons, 444 Castro Street, Suite 900, Mountain View, California, 94041, USA.

#AVERTISSEMENT
#Pour un affichage optimal, il est recommandé de mettre la fenêtre en plein écran.

#Description :
#Ce programme a pour objectif de mettre en évidence l'effet d'échantionnage, ainsi que l'effet de filtrage sur un signal analogique.

# La promotion 2017/2018 de l'ENS de Lyon a simplement rajouté un slider permettant de voir l'influence de la fréquence d'échantillonage de manière interactive.
# Nous avons aussi enlevé la fonction filtrage, car nous n'en voulions pas, ainsi que le choix d'un signal carré.


#Francis Pagaud et Gauthier Legrand de l'ENS de Lyon promo 2020 ont adapte ce code pour illustrer la LP23 sans experience

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.widgets as wd
import matplotlib
matplotlib.rc('xtick', labelsize=20)
matplotlib.rc('ytick', labelsize=20)
matplotlib.rcParams.update({'font.size': 22})
from matplotlib.widgets import RadioButtons, Slider, Button, CheckButtons
import pandas

# =============================================================================
# --- Définitions des paramètres sur lesquels on peut agir --------------------
# =============================================================================

### Signal analogique ###
fe = 10 # fréquence du signal
Amplitude = 1.0 # Amplitude du signal

### Signal numérique d'entrée ###
Tacquisition = 0.8 # durée d'acquisition
fechantillonnage = 41 # fréquence d'échantillonnage
quantification = 0
p_filtre = 0



# =============================================================================
# --- Fonction intermediaire qui échantillone le signal -----------------------
# =============================================================================

def TableSignalEntree(fe,fech,Tacq,A, quantification = 0) :
    '''
    fe : fréquence du signal
    fech : fréquence d'échantillonage
    Tacq : Temps d'acquisition
    A : amplitude du signal
    '''

    Npoint = int(fech*Tacq+1)
    temps=np.linspace(1,Npoint,Npoint)/fech

    if quantification == 0 :
        signal=A*np.cos(2*np.pi*fe*temps)
    else :
        signal= np.round(A*np.cos(2*np.pi*fe*temps)/quantification)*quantification
    return temps,signal

#### Etude spectrale ###

# =============================================================================
# --- Fonction intermediaire qui effectue une derivation ou un filtrage --------
# =============================================================================

def derivation(t, signal):
    '''
    t : vecteur temps
    signal : vecteur du signal correspondant
    le temps d'echantillonnage est suppose constant
    '''
    N, dt = len(t), t[1]-t[0] #Nombre de point et intervalle de temps
    derivee = np.zeros(N-1)
    if p_filtre == 0 :
        signal_bis = signal
    else :
        signal_bis = filtrage(signal, p_filtre)
    for k in range(N-1) :
        derivee[k] = (signal_bis[k+1]-signal_bis[k])/dt
    t_prime = t[0:N-1] + dt/2
    return t_prime, derivee


def filtrage(signal, P) : #moyenne glissee sur 2P+1 points (P points a gauche et P points a droite)
    N = len(signal)
    signal_filtre = np.copy(signal)
    for k in range(N) :
        if k >= P and k < N-P :
            signal_filtre[k] = np.mean(signal[k-P:k+P])
    return signal_filtre



### Critere de Shannon-Nyquist et etude spectrale
# =============================================================================
# --- Code principal-----------------------------------------------------------
# =============================================================================

#Table du signal analogique (en fait échantilloné avec une très grande fréquence : aucun problème de repliement de spectre)
Table_vrai_signalx, Table_vrai_signaly = TableSignalEntree(fe,200*fe,Tacquisition,Amplitude)

#Table du signal échantilloné
Tablex,Tabley = TableSignalEntree(fe,fechantillonnage,Tacquisition,Amplitude,quantification)


# derivation des signaux
t_diff_vrai , deriv_vrai = derivation(Table_vrai_signalx, Table_vrai_signaly)
t_diff , deriv = derivation(Tablex, Tabley)




fa,(ax1, ax2)=plt.subplots(nrows = 2)
plt.subplots_adjust(left=0.15, bottom=0.25,hspace=0.4)


ax1.set_title(r'Echantillonnage (frequence echantillonnage='+str(fechantillonnage)+' Hz, frequence='+str(fe)+' Hz)',fontsize=24)
Msize = 10; Mtype = 'x'


#Titres et dimensions des axes
ax1.set_ylim(-1.5*Amplitude,1.5*Amplitude)
ax1.set_ylabel(r'Amplitude',fontsize=24)
ax1.set_xlim(0,Tacquisition)
ax1.set_xlabel(r'Temps (s)',fontsize=24)

# bruit de quantification



#Trace le signal "analogique", le signal échantilloné et le signal filtré
a, = ax1.plot(Table_vrai_signalx,Table_vrai_signaly,'--r',color='blue',linewidth=1,label="Analogique")
l, = ax1.plot(Tablex,Tabley,color='red',marker=Mtype,markersize=Msize,linewidth=2,label="Numerique")
c, = ax1.plot(Tablex,Tabley,color='green',linewidth=0,label="Filtré")
ax1.set_xlabel("Temps (s)",fontsize=18)
ax1.set_ylabel("Amplitude",fontsize=18)
ax1.set_title(r'Echantillonnage (frequence echantillonnage='+str(fechantillonnage)+' Hz, frequence='+str(fe)+' Hz)',fontsize=18)
ax1.set_xlim([0,Tacquisition])
ax1.grid(True)

b, = ax2.plot(t_diff_vrai,deriv_vrai,'--r',color='blue',linewidth=1,label="Analogique")
h, = ax2.plot(t_diff,deriv,color='red',marker=Mtype,markersize=Msize,linewidth=2,label="Numerique")
ax2.set_xlabel("Temps (s)",fontsize=18)
ax2.set_ylabel("Derivee du signal",fontsize=18)
ax2.set_xlim([0,Tacquisition])
ax2.set_ylim([-100,100])
ax2.grid(True)

#Définition du slider et fonction de mise-à-jour du plot
axfreqech = plt.axes([0.4, 0.075, 0.5, 0.03])
freqech = wd.Slider(axfreqech, 'Freq (Hz)', 8, 500, valinit=fechantillonnage, valfmt = '%0.1f')

ax_tacq = plt.axes([0.4, 0.04, 0.5, 0.03])
t_tot = wd.Slider(ax_tacq, '$T_{acq} (s)$', 0.1, 2, valinit=Tacquisition, valfmt = '%0.1f')

def update(val):
    freq = freqech.val
    Tacq = t_tot.val
    Table_vrai_signalx, Table_vrai_signaly = TableSignalEntree(fe,100*fe,Tacq,Amplitude)
    t_diff_vrai , deriv_vrai = derivation(Table_vrai_signalx, Table_vrai_signaly)
    frequ = round(freq, 2)
    Tablex1,Tabley1 = TableSignalEntree(fe,frequ,Tacq,Amplitude, quantification)
    t_diff1 , deriv1 = derivation(Tablex1, Tabley1)
    ax1.set_xlim([0,Tacq])
    ax2.set_xlim([0,Tacq])
    a.set_xdata(Table_vrai_signalx)
    a.set_ydata(Table_vrai_signaly)
    l.set_ydata(Tabley1)
    l.set_xdata(Tablex1)
    h.set_ydata(deriv1)
    h.set_xdata(t_diff1)
    b.set_ydata(deriv_vrai)
    b.set_xdata(t_diff_vrai)
    ax1.set_title(r'Echantillonnage (frequence echantillonnage='+str(round(frequ, 1))+' Hz, frequence='+str(fe)+' Hz)',fontsize=18)
    if p_filtre != 0 : #a supprimer si on veut retirer le fitlrage
        c.set_ydata(filtrage(Tabley1, p_filtre))
        c.set_xdata(Tablex1)
        c.set_linewidth(2)
    fa.canvas.draw_idle()

def change_quantif(label):
    global quantification
    if label=='0.1':
        quantification = 0.001
    elif label=='5':
        quantification = 0.05
    elif label=='10':
        quantification = 0.1
    else :
        quantification = 0.5

def change_fitlre(label):
    global p_filtre
    if label=='Désactivée':
        p_filtre = 0
    elif label=='3 points':
        p_filtre = 1
    elif label=='9 points':
        p_filtre = 4



rax = plt.axes([0.01, 0.01, 0.1, 0.15])
plt.text(-0.1, 1.3, 'Quantification')
plt.text(0, 1.1, '(% du max)')
radio = RadioButtons(rax, ('0.1', '5', '10', '50'))
radio.on_clicked(change_quantif)
radio.on_clicked(update)

### a supprimer si on veut retirer le filtrage
rax_bis = plt.axes([0.15, 0.01, 0.12, 0.15])
plt.text(-0.1, 1.1, 'Moyenne glissée')
radio_bis = RadioButtons(rax_bis, ('Désactivée', '3 points', '9 points'))
radio_bis.on_clicked(change_fitlre)
radio_bis.on_clicked(update)


freqech.on_changed(update)
t_tot.on_changed(update)

mng = plt.get_current_fig_manager()     #Plein ecran
mng.window.showMaximized()
plt.show()
