Skip to article frontmatterSkip to article content

Exemples Intéractifs: Numérisation

Introduction\textbf{Introduction}

Eˊchantillonnage\textbf{Échantillonnage}

L’échantillonnage consiste de la lecture d’un signal à intervalles réguliers. Dans un cas idéal on serait capable de mesurer une valeur pour chaque instant du domaine temporel. Cela n’est pas réalisable en pratique.

Effet du chevauchement et Spectre d’Échantillonnage

Cette section illustre l’effet du chevauchement lors de l’échantillonnage d’un signal triangulaire. Vous pouvez ajuster la fréquence du signal et la fréquence d’échantillonnage pour observer :

  1. Dans le domaine temporel :

    • Le signal continu (en bleu).
    • Les échantillons obtenus (points rouges).
  2. Dans le domaine fréquentiel :

    • Le spectre du signal continu.
    • Les répliques du spectre causées par l’échantillonnage.

Explorez les paramètres pour observer comment le chevauchement se manifeste lorsque (fs)( f_s) est insuffisante pour capturer correctement le signal.

Source
import numpy as np
import matplotlib.pyplot as plt
from ipywidgets import interact, FloatSlider

def triangular_wave(frequency, t):
    return 2 * (np.abs(2 * ((t * frequency) % 1) - 1)) - 1

def plot_chevauchement_spectral(signal_frequency=5, sampling_frequency=20):
    T = 1.0       # Durée (1s)
    N = 2000      # Points pour la FFT
    dt = T / N
    t = np.linspace(0, T, N, endpoint=False)

    # Signal continu + échantillonnage
    x_continu = triangular_wave(signal_frequency, t)
    t_sample = np.arange(0, T, 1/sampling_frequency)
    x_sample = triangular_wave(signal_frequency, t_sample)

    # FFT
    X = np.fft.fftshift(np.fft.fft(x_continu))
    freq_axis = np.fft.fftshift(np.fft.fftfreq(N, d=dt))
    X_mag = np.abs(X) / N

    # Création de la figure
    fig, axes = plt.subplots(1, 2, figsize=(12, 4))

    # 1) Domaine temporel
    axes[0].plot(t, x_continu, label='Signal continu', lw=2)
    axes[0].stem(t_sample, x_sample, linefmt='r', markerfmt='ro', basefmt=' ', label='Échantillons')
    axes[0].set_title(f"Signal triangulaire : f={signal_frequency} Hz, fs={sampling_frequency} Hz")
    axes[0].set_xlabel("Temps (s)")
    axes[0].set_ylabel("Amplitude")
    axes[0].set_ylim(-1.2, 1.2)
    axes[0].grid(True)
    axes[0].legend()

    # 2) Domaine fréquentiel
    Fs = sampling_frequency
    axes[1].plot(freq_axis, X_mag, label='Spectre principal', lw=2, color='blue')

    # # Bande de Nyquist
    # axes[1].axvspan(-Fs/2, Fs/2, color='y', alpha=0.2, label='Bande de Nyquist')
    # axes[1].axvline(x=-Fs/2, color='g', linestyle='--', alpha=0.5, label='-Fs/2')
    # axes[1].axvline(x=Fs/2, color='g', linestyle='--', alpha=0.5, label='Fs/2')

    # Répliques du spectre autour de k*Fs
    nb_replicas = 3
    for k in range(-nb_replicas, nb_replicas + 1):
        if k != 0:
            axes[1].plot(freq_axis + k*Fs, X_mag, 'r--', alpha=0.4,
                         label='Échantillons' if k == 1 else None)

    axes[1].set_title("Spectre échantillonné et répliques")
    axes[1].set_xlabel("Fréquence (Hz)")
    axes[1].set_ylabel("Amplitude")
    axes[1].set_xlim(-nb_replicas * Fs, nb_replicas * Fs)
    axes[1].set_ylim(0, 1.2 * X_mag.max())
    axes[1].grid(True)
    axes[1].legend(loc='upper right')
    plt.tight_layout()
    plt.show()

interact(
    plot_chevauchement_spectral,
    signal_frequency=FloatSlider(value=5, min=1, max=30, step=1, description="Signal (Hz)"),
    sampling_frequency=FloatSlider(value=20, min=5, max=60, step=1, description="Échantillonnage (Hz)")
)
Loading...

1. Domaine temporel (graphique de gauche)

  • La courbe bleue représente le signal triangulaire continu.
  • Les points rouges (et leurs traits verticaux) représentent les échantillons du signal, obtenus en prélevant régulièrement le signal continu à la fréquence d’échantillonnage (fs).
  • Lorsque la fréquence d’échantillonnage est trop faible, on observe un chevauchement temporel : les échantillons ne capturent plus correctement la forme réelle du signal.

2. Domaine fréquentiel (graphique de droite)

  • La courbe bleue au centre illustre le spectre du signal triangulaire (FFT de la version continue, centrée via fftshift).
  • Les courbes rouges en pointillé représentent les répliques de ce spectre autour des multiples de la fréquence d’échantillonnage (±fs,±2fs,...)(\pm f_s, \pm 2f_s, ...).
  • Quand (fs)( f_s) (la fréquence d’échantillonnage) est suffisamment élevée, les répliques ne chevauchent pas le spectre principal : il n’y a pas de chevauchement.
  • Si (fs)( f_s) est trop faible, alors ces répliques viennent se superposer (ou se rapprocher) du spectre principal : il se produit un chevauchement fréquentiel.

#Modulations Binaires de Porteuse de Base

Modulation PAM\textbf{Modulation PAM}

La modulation PAM (Pulse Amplitude Modulation) consiste à représenter un signal message sous forme d’impulsions en multipliant le signal par un train d’impulsions périodiques.

Cette section illustre :

  1. Signal message et onde carrée :

    • Une sinusoïde représentant le message.
    • Un train d’impulsions périodiques contrôlé par le rapport cyclique.
  2. Signal PAM :

    • Résultat de la multiplication du signal message par les impulsions.

Source
import numpy as np
import matplotlib.pyplot as plt
from ipywidgets import interact, FloatSlider

# Paramètres globaux pour la simulation
fs = 2000    # Fréquence d'échantillonnage "interne" (haute) pour la simulation
t_max = 1.0  # Durée d'observation (1 seconde)
t = np.linspace(0, t_max, int(fs*t_max), endpoint=False)

def pam_square_wave(
    message_freq=5,   # Fréquence du signal message
    sampling_freq=10, # Fréquence de la porteuse/échantillonnage
    duty_cycle=0.2    # Proportion de la période où l'onde carrée est à 1
):
    """
    PAM avec impulsions de type onde carrée.
    - message_freq : fréq. du signal message (sinus)
    - sampling_freq : "fréquence" du train d'impulsion
    - duty_cycle : rapport cyclique de l'onde carrée (entre 0 et 1)
    """

    # ---------- 1) Génération du signal message (sinusoïde) ----------
    message = np.sin(2 * np.pi * message_freq * t)

    # ---------- 2) Génération de l'onde carrée pour l'échantillonnage ----------
    # Période d'échantillonnage
    Ts = 1.0 / sampling_freq

    # Création d'un vecteur "onde carrée" (même dimension que t)
    # Méthode : pour chaque intervalle [k*Ts, (k+1)*Ts),
    # on met 1 pendant 'duty_cycle'% du temps, sinon 0.
    square_wave = np.zeros_like(t)
    for k in range(int(t_max * sampling_freq)):
        start_idx = int(k * Ts * fs)  # indice début de période
        end_idx   = int((k+1) * Ts * fs)  # indice fin de période
        if end_idx > len(t):
            end_idx = len(t)

        pulse_width = duty_cycle * (end_idx - start_idx)
        pulse_end   = start_idx + int(pulse_width)

        # Remplit de 1 dans [start_idx, pulse_end)
        square_wave[start_idx:pulse_end] = 1

    # ---------- 3) Réalisation de la modulation PAM ----------
    # Multiplication point à point du message par l'onde carrée
    pam_signal = message * square_wave

    # ---------- 4) Affichage ----------
    plt.figure(figsize=(10, 5))

    # a) Affichage du signal message + onde carrée
    plt.subplot(2, 1, 1)
    plt.plot(t, message, label='Signal message', lw=1.5)
    plt.plot(t, square_wave, label='Onde carrée (Train d\'impulsions)', alpha=0.7)
    plt.ylim(-1.2, 1.2)
    plt.title(f"PAM - Signal message & onde carrée\nf_msg={message_freq}Hz, f_sampling={sampling_freq}Hz, duty={duty_cycle}")
    plt.xlabel("Temps (s)")
    plt.ylabel("Amplitude")
    plt.grid(True)
    plt.legend()

    # b) Affichage du signal PAM
    plt.subplot(2, 1, 2)
    plt.plot(t, pam_signal, color='red', label='Signal PAM', lw=1.5)
    plt.ylim(-1.2, 1.2)
    plt.xlabel("Temps (s)")
    plt.ylabel("Amplitude")
    plt.grid(True)
    plt.legend()

    plt.tight_layout()
    plt.show()

# ----- Interface interactive -----
interact(
    pam_square_wave,
    message_freq=FloatSlider(value=5, min=1, max=30, step=1, description="f_msg"),
    sampling_freq=FloatSlider(value=10, min=2, max=60, step=1, description="f_samp"),
    duty_cycle=FloatSlider(value=0.2, min=0.01, max=0.9, step=0.01, description="duty")
);
Loading...

Modulation PWM (Pulse Width Modulation)\textbf{Modulation PWM (Pulse Width Modulation)}

La modulation PWM (Pulse Width Modulation) consiste à moduler la largeur des impulsions en fonction de l’amplitude d’un signal message. Cela est réalisé en comparant le signal message (m(t))(m(t)) avec une onde triangulaire (p(t))(p(t)).


Résultats visualisés

  1. Signal message et onde triangulaire :

    • Le signal message (m(t))(m(t)) varie entre 0 et 1.
    • L’onde triangulaire (s(t))(s(t)) est utilisée comme référence de comparaison.
  2. Signal PWM :

    • Un signal binaire obtenu en mettant 1 lorsque m(t)>s(t)m(t) > s(t), sinon 0.

Instructions interactives

  • fmsgf_{\text{msg}} : Fréquence du signal message (en Hz).
  • fcarf_{\text{car}} : Fréquence de l’onde triangulaire (en Hz).
Source
import numpy as np
import matplotlib.pyplot as plt
from ipywidgets import interact, FloatSlider

# Paramètres globaux pour la simulation
fs = 2000    # Fréquence d'échantillonnage interne (pour la simulation)
t_max = 1.0  # Durée d'observation
t = np.linspace(0, t_max, int(fs*t_max), endpoint=False)

def pwm_modulation(
    message_freq=5,    # Fréquence du message (en Hz)
    carrier_freq=50    # Fréquence de l'onde triangulaire (en Hz)
):
    """
    PWM : On compare le signal message (entre 0 et 1) avec une onde triangulaire
    pour générer un signal à largeur d'impulsion variable.
    """

    # 1) Générer le signal message (amplitude entre 0 et 1)
    #    - Variation sinus : centrée en 0.5, amplitude 0.5 => [0,1]
    message = 0.5 + 0.5 * np.sin(2 * np.pi * message_freq * t)

    # 2) Générer l'onde triangulaire (0 -> 1 sur chaque période)
    #    Période de l'onde triangulaire
    Tc = 1.0 / carrier_freq
    tri_wave = (t % Tc) / Tc  # Remise à zéro toutes les Tc

    # 3) Comparaison -> PWM
    #    - On met 1 si message > tri, sinon 0
    pwm_signal = (message > tri_wave).astype(float)

    # 4) Tracé
    plt.figure(figsize=(10, 5))

    # a) Signal message + onde triangulaire
    plt.subplot(2,1,1)
    plt.plot(t, message, label='Message (0 à 1)', lw=1.5)
    plt.plot(t, tri_wave, 'r', label='Onde triangulaire', alpha=0.7)
    plt.ylim(-0.1, 1.1)
    plt.title(f"Signal Message vs. Onde Triangulaire\nf_msg={message_freq}Hz, f_car={carrier_freq}Hz")
    plt.xlabel("Temps (s)")
    plt.legend()
    plt.grid(True)

    # b) Signal PWM
    plt.subplot(2,1,2)
    plt.plot(t, pwm_signal, 'g', label='Signal PWM', lw=1.5)
    plt.ylim(-0.1, 1.1)
    plt.title("Signal PWM (largeur d'impulsion modulée)")
    plt.xlabel("Temps (s)")
    plt.legend()
    plt.grid(True)

    plt.tight_layout()
    plt.show()

# ----- Interface interactive -----
interact(
    pwm_modulation,
    message_freq=FloatSlider(value=5, min=1, max=30, step=1, description="f_msg"),
    carrier_freq=FloatSlider(value=50, min=10, max=200, step=10, description="f_car")
)
Loading...

Modulation PPM (Pulse Position Modulation)\textbf{Modulation PPM (Pulse Position Modulation)}

La modulation PPM (Pulse Position Modulation) consiste à placer une impulsion unique dans chaque période d’un signal de référence. La position de l’impulsion dépend de l’amplitude du signal message (m(t))(m(t)).


Résultats visualisés

  1. Signal message (m(t))(m(t)) :

    • Le signal message varie entre 0 et 1.
  2. Signal PPM :

    • Une impulsion est placée dans chaque période T=1fcarT = \frac{1}{f_{\text{car}}}.
    • La position de l’impulsion est proportionnelle à l’amplitude de (m(t))(m(t)).

Instructions interactives

  • fmsgf_{\text{msg}} : Fréquence du signal message (en Hz).
  • fcarf_{\text{car}} : Fréquence de la porteuse ou référence temporelle (en Hz).
Source
import numpy as np
import matplotlib.pyplot as plt
from ipywidgets import interact, FloatSlider

# Paramètres pour la simulation
fs = 2000    # "Haute" fréquence pour la simulation
t_max = 1.0  # Durée d'observation en secondes
t = np.linspace(0, t_max, int(fs*t_max), endpoint=False)

def ppm_example(message_freq=5, carrier_freq=10):
    """
    PPM :
      - On divise le temps en périodes T = 1/carrier_freq.
      - Dans chaque période, on place UNE impulsion.
      - Sa position dépend de l’amplitude du message (entre 0 et 1).
    """

    # 1) Générer le signal message entre 0 et 1
    message = 0.5 + 0.5 * np.sin(2 * np.pi * message_freq * t)

    # 2) Nombre de périodes pendant la durée t_max
    Ts = 1.0 / carrier_freq
    nb_periods = int(t_max * carrier_freq)

    # 3) Génération du signal PPM initialisé à 0
    ppm_signal = np.zeros_like(t)

    # 4) Placer une impulsion par période
    #    La position de l’impulsion est proportionnelle à la valeur du message
    max_delay_fraction = 0.8  # évite que l'impulsion ne dépasse la fin de la période
    for k in range(nb_periods):
        start_idx = int(k * Ts * fs)
        end_idx   = int((k+1) * Ts * fs)
        if end_idx >= len(t):
            break

        # Amplitude du message au début de la période
        amp_val = message[start_idx]

        # Décalage en échantillons
        delay_samples = int(amp_val * max_delay_fraction * (end_idx - start_idx))
        pulse_idx = start_idx + delay_samples

        if pulse_idx < end_idx:
            ppm_signal[pulse_idx] = 1

    # 5) Affichage
    plt.figure(figsize=(10, 5))

    # a) Signal message
    plt.subplot(2, 1, 1)
    plt.plot(t, message, label='Message (0 -> 1)', lw=1.5)
    plt.ylim(-0.1, 1.1)
    plt.title(f"Signal Message : f_msg = {message_freq} Hz")
    plt.xlabel("Temps (s)")
    plt.legend()
    plt.grid(True)

    # b) Signal PPM
    plt.subplot(2, 1, 2)
    plt.plot(t, ppm_signal, 'r', label='Signal PPM', lw=1.5)
    plt.ylim(-0.2, 1.2)
    plt.title(f"Signal PPM : f_car = {carrier_freq} Hz")
    plt.xlabel("Temps (s)")
    plt.legend()
    plt.grid(True)

    plt.tight_layout()
    plt.show()

# Interface interactive
interact(
    ppm_example,
    message_freq=FloatSlider(value=5, min=1, max=30, step=1, description="f_msg"),
    carrier_freq=FloatSlider(value=10, min=5, max=50, step=5, description="f_car")
)
Loading...

Explication du résultat ci-dessus (Modulation PPM)

  • Le temps total est découpé en périodes de durée :
    Ts=1fcarT_s = \frac{1}{f_{\text{car}}}
  • Ces périodes sont numérotées :
    k=0,1,2,k = 0, 1, 2, \dots
  • Au début de chaque période, on lit l’amplitude du message.
  • Cette amplitude est convertie en un décalage (en échantillons), qui détermine la seule impulsion est placée à l’intérieur de la période.
  • Signal message fort (proche de 1) : l’impulsion est placée vers la fin de la période.
  • Signal message faible (proche de 0) : l’impulsion est placée au début de la période.

Caractéristiques principales :

  • La largeur de l’impulsion est constante.
  • La position de l’impulsion varie proportionnellement à l’amplitude du signal.

Modulation PCM (Pulse Code Modulation)\textbf{Modulation PCM (Pulse Code Modulation)}

La modulation PCM (Pulse Code Modulation) est un processus de conversion d’un signal analogique en un signal numérique par trois étapes principales : l’échantillonnage, la quantification, et la reconstruction.

  • nbitsn_{\text{bits}} : Nombre de bits pour la quantification (contrôle la précision de la quantification).
  • NeˊchantillonsN_{\text{échantillons}} : Nombre d’échantillons utilisés pour la reconstruction (contrôle la fréquence d’échantillonnage).

Ajustez les paramètres pour observer l’impact de la fréquence d’échantillonnage et du nombre de bits sur la qualité du signal reconstruit.

Source
# Importation des bibliothèques nécessaires
import numpy as np
import matplotlib.pyplot as plt
import ipywidgets as widgets
from IPython.display import display, clear_output
import math  # Importation du module math pour la fonction ceil

# Activation des graphiques inline
%matplotlib inline

# Définition de la fonction principale pour le PCM interactif
def pcm_interactif(n_bits=3, num_samples=50):
    """
    Fonction interactive pour démontrer le processus de Modulation par Code de Pulsations (PCM).

    Paramètres :
    - n_bits : nombre de bits pour la quantification
    - num_samples : nombre d'échantillons
    """
    # Effacer les sorties précédentes
    clear_output(wait=True)

    # Paramètres fixes
    A = 1            # Amplitude du signal
    f = 5            # Fréquence du signal en Hz
    t_max = 1        # Durée du signal en secondes
    fs_analog = 1000 # Fréquence d'échantillonnage analogique en Hz

    # Calcul de la fréquence d'échantillonnage
    fs = num_samples / t_max  # Fréquence d'échantillonnage en Hz

    # Génération du signal analogique
    t = np.arange(0, t_max, 1/fs_analog)
    signal_analog = A * np.sin(2 * np.pi * f * t)

    # Échantillonnage du signal
    Ts = 1/fs
    t_ech = np.arange(0, t_max, Ts)
    signal_echantillonne = A * np.sin(2 * np.pi * f * t_ech)

    # Quantification des échantillons
    q_levels = 2 ** n_bits  # Nombre de niveaux de quantification
    A_max = A
    A_min = -A

    delta = (A_max - A_min) / q_levels  # Taille d'un pas de quantification

    # Quantification des échantillons
    signal_quantifie = np.floor((signal_echantillonne - A_min) / delta) * delta + delta / 2 + A_min
    signal_quantifie = np.clip(signal_quantifie, A_min, A_max - delta)

    # Reconstruction du signal
    if fs == 0:
        repeat_factor = 1
    else:
        repeat_factor = math.ceil(fs_analog / fs)  # Utilisation de ceil pour éviter les longueurs insuffisantes
    signal_reconstruit = np.repeat(signal_quantifie, repeat_factor)
    signal_reconstruit = signal_reconstruit[:len(t)]  # Ajustement de la longueur

    # Affichage des graphiques
    plt.figure(figsize=(14, 6))
    plt.plot(t, signal_analog, label='Signal analogique', color='blue')
    plt.stem(t_ech, signal_echantillonne, linefmt='r--', markerfmt='ro', basefmt='k-', label='Échantillons')  # Suppression de 'use_line_collection'
    plt.step(t, signal_reconstruit, where='mid', label='Signal reconstruit (PCM)', color='green', linestyle='-')
    plt.title(f'PCM avec {n_bits} bits de quantification et {num_samples} échantillons')
    plt.xlabel('Temps (s)')
    plt.ylabel('Amplitude')
    plt.legend()
    plt.grid(True)
    plt.show()

    # Affichage des informations sur la quantification
    print(f"**Paramètres actuels :**")
    print(f"- Fréquence du signal (f) : {f} Hz")
    print(f"- Fréquence d'échantillonnage (fs) : {fs:.2f} Hz")
    print(f"- Nombre de bits (n_bits) : {n_bits}")
    print(f"- Nombre d'échantillons : {num_samples}")
    print(f"- Durée du signal : {t_max} s")
    print()
    print(f"**Quantification :**")
    print(f"- Nombre de niveaux de quantification (q_levels) : {q_levels}")
    print(f"- Taille d'un pas de quantification (delta) : {delta:.5f}")
    print()
    # print(f"**Échantillons quantifiés :**")
    # for i, val in enumerate(signal_quantifie):
    #     if n_bits <= 32:
    #         code = format(int((val - A_min) / delta), f'0{n_bits}b')
    #     else:
    #         # Pour n_bits > 32, l'encodage binaire peut être trop long, donc on l'indique comme N/A
    #         code = 'N/A'
    #     print(f"Échantillon {i+1}: Valeur = {val:.5f}, Code = `{code}`")

# Création des widgets interactifs
bits_widget = widgets.IntSlider(value=3, min=1, max=8, step=1, description='Bits:')
samples_widget = widgets.IntSlider(value=50, min=10, max=125, step=1, description='Échantillons:')

# Liaison des widgets à la fonction interactive
interactive_pcm = widgets.interactive(pcm_interactif, n_bits=bits_widget, num_samples=samples_widget)

# Affichage des widgets et de la fonction interactive
display(interactive_pcm)
Loading...