{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "# Requirements\n",
    "! pip install scipy\n",
    "! pip install numpy\n",
    "! pip install matplotlib"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "from scipy.signal import tf2zpk, freqz, dimpulse, dlsim\n",
    "from scipy.special import binom"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Lab 3 - Structure des systèmes linéaires"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 1. Fonction de transfert"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Considérons un système numérique tel que pour un signal d'entrée $x[n]$ on obtient une sortie $y[n]$. Lorsqu'un système est linéaire et invariant dans le temps (LIT), on peut observer les propriétés suivantes: \n",
    "\n",
    "\n",
    "* Si deux entrées $x_1[n]$ et $x_2[n]$ engendrent deux sorties $y_1[n]$ et $y_2[n]$, alors $k*x_1[n] + l*x_2[n]$ engendrera $k*y_1[n] + l*y_2[n]$ (linéarité). \n",
    "* S'il y a invariance dans le temps, une translation de l'entrée $x[n]\\rightarrow x[n-k]$ se traduira par une même translation dans le temps de la sortie $y[n]\\rightarrow y[n-k]$.\n",
    "  \n",
    "Si les hypothèses de linéarité et d'invariance sont vérifiées, on peut caractériser le système par sa **réponse impulsionnelle**, $h[n]$. C'est le signal qu'on obtient en sortie si on applique en entrée une **impulsion de Dirac** $\\delta[n]$.\n",
    "\n",
    "En même temps, la **réponse impulsionnelle** nous permet de définir la **fonction de transfert** d'un système. La *fonction de transfert* caractérise la dynamique du système, et elle peut être calculée comme la *transformée en Z* de sa *réponse impulsionnelle*.\n",
    "\n",
    "\\begin{equation*}\n",
    "H(z) = Z\\{h\\left[n\\right]\\} = \\sum_{n=-\\infty}^{\\infty}h\\left[n\\right]z^{-n}\n",
    "\\end{equation*}\n",
    "\n",
    "Cette équation nous permet alors, si on connaît $X(z)$ et $H(z)$, de calculer $Y(z)$ et d'en déduire $y[n]$. En effet, la *transformée en Z* de la sortie du système peut alors être calculée comme:\n",
    "\n",
    "\\begin{equation*}\n",
    "Y(z) = X(z)H(z)\n",
    "\\end{equation*}\n",
    "\n",
    "Cependant les systèmes LIT d'intérêt pratique sont généralement ceux qu'on peut définir par une *équation linéaire aux différences finies* avec coefficients constants:\n",
    "\n",
    "\\begin{equation*}\n",
    "\\sum_{k=0}^{N_P} a_k y[n-k] = \\sum_{k=0}^{N_Q}b_k x[n-k]\n",
    "\\end{equation*}\n",
    "\n",
    "qui nous permet de calculer directement la sortie du système sans devoir passer par le calcul de sa *transformée en Z*. En effet en utilisant les propriétés de la *transformée en Z*, on peut determiner rapidement $H(z)$ comme:\n",
    "\n",
    "\\begin{equation*}\n",
    "H(z) = \\frac{Y(z)}{X(z)}=\\frac{\\sum_{k=0}^{N_Q}b_k z^{-k}}{\\sum_{k=0}^{N_P} a_k z^{-k}}= \\frac{b_0\\prod_{k=1}^{N_Q}(1-c_k z^{-1})}{a_0\\prod_{k=1}^{N_P}(1-p_k z^{-1})}\n",
    "\\end{equation*}\n",
    "\n",
    "Considérez maintenant le système caractérisé par l'équation linéaire aux différences finies suivante:\n",
    "\\begin{equation*}\n",
    "    y[n] = 2x[n] - \\frac{3}{4}x[n-1] + \\frac{3}{4}y[n-1]- \\frac{1}{8}y[n-2]\n",
    "\\end{equation*}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 1. Déterminer $H(z)$ sous la forme $\\frac{b_0\\prod_{k=1}^{N_Q}(1-c_k z^{-1})}{a_0\\prod_{k=1}^{N_P}(1-p_k z^{-1})}$."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 2. Trouver les pôles et les zéros de $H(z)$ numériquement. Vous pouvez vous aider de la fonction [tf2zpk](https://docs.scipy.org/doc/scipy/reference/generated/scipy.signal.tf2zpk.html)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Votre réponse...\n",
    "zs = []\n",
    "ps = []\n",
    "\n",
    "print('Pôles:\\n{}'.format(\"\".join([\"\\t{:.4f}\\n\".format(p) for p in ps ])))\n",
    "print('Zéros:\\n{}'.format(\"\".join([\"\\t{:.4f}\\n\".format(z) for z in zs ])))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 3. Afficher les pôles et les zéros  de $H(z)$ dans le plan complexe."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def plot_pz(p, z):\n",
    "    \n",
    "    t = np.linspace(0, 2*np.pi, 100)\n",
    "    \n",
    "    plt.figure(figsize=(5,5))\n",
    "    plt.plot(np.cos(t), np.sin(t), linewidth=1)\n",
    "    plt.vlines(0, ymin=-1.2, ymax=1.2)\n",
    "    plt.hlines(0, xmin=-1.2, xmax=1.2)\n",
    "    \n",
    "    plt.scatter(p.real, p.imag, marker='x', c='r', label='pôles')\n",
    "    plt.scatter(z.real, z.imag, marker='o', c='r', label='zéros')\n",
    "    \n",
    "    plt.legend()\n",
    "    plt.title(\"Pôles et zéros\")\n",
    "    plt.xlabel(\"Réel\")\n",
    "    plt.ylabel(\"Imaginaire\")\n",
    "    \n",
    "plot_pz(ps, zs)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 4. Tracer la réponse en fréquence (amplitude et phase) du filtre $H(z)$. Utiliser $N=1024$ points. Vous pouvez vous aider de la fonction [freqz](https://docs.scipy.org/doc/scipy/reference/generated/scipy.signal.freqz.html). Utilisez aussi la fonction `plot_freqz` ci-dessous pour vous aider."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def plot_freqz(w, h, title=''):\n",
    "    fig, ax = plt.subplots(2, 1, figsize=(10, 6))\n",
    "\n",
    "    ax[0].set_title(title)\n",
    "    ax[0].plot(w/np.pi, 20 * np.log10(abs(h)), 'b')\n",
    "    ax[0].grid('on')\n",
    "    ax[0].set_ylabel('Amplitude [dB]')\n",
    "    ax[0].set_xlabel('Fréquence [$\\pi$ rad/sample]')\n",
    "\n",
    "    ax[1].plot(w/np.pi, np.angle(h), 'g')\n",
    "    ax[1].grid('on')\n",
    "    ax[1].set_ylabel('Phase (radian)')\n",
    "    ax[1].set_xlabel('Fréquence [$\\pi$ rad/sample]')\n",
    "    \n",
    "    plt.tight_layout()\n",
    "    plt.show()\n",
    "\n",
    "\n",
    "# Complétez le code suivant\n",
    "w, h = None, None\n",
    "##############\n",
    "# Votre code #\n",
    "##############\n",
    "\n",
    "plot_freqz(w, h,'Réponse en fréquence de $H(z)$')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 5. Déterminer la réponse de ce système à la fonction échelon unité (réelle elle aussi)."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 6. Soit $e[n]$ la fonction échelon unité défini sur $n\\in[-32,32]$. Tracer les 64 premiers points de la réponse impulsionelle $h[n]$ ainsi que la réponse finale du système à la fonction $e[n]$. Aidez vous de la fonction [dlsim](https://docs.scipy.org/doc/scipy/reference/generated/scipy.signal.dlsim.html) et de `plot_signal` ci-dessous."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def plot_signal(t, y, title=''):\n",
    "    plt.figure(figsize=(10, 5))\n",
    "    plt.stem(t, y)\n",
    "    plt.title(title)\n",
    "    plt.ylabel('y[n]')\n",
    "    plt.xlabel('n [sample]')\n",
    "\n",
    "\n",
    "# Complétez le code ci-dessous\n",
    "n, u = None, None\n",
    "##############\n",
    "# Votre code #\n",
    "##############\n",
    "\n",
    "plot_signal(n, u, title='Signal Echelon Unité $e[n]$')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Complétez le code ci-dessous\n",
    "t, y = None, None\n",
    "##############\n",
    "# Votre code #\n",
    "##############\n",
    "plot_signal(t, y, title='Réponse impulsionnelle h[n]')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Complétez le code ci-dessous\n",
    "##############\n",
    "# Votre code #\n",
    "##############\n",
    "n, y = None, None\n",
    "plot_signal(n, y, title='Réponse $x[n]$ * $h[n]$')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 7. Comparez les deux réponses obtenues. Qu'observez vous?\n",
    "\n",
    "Hint: Dans l’expression de s[n], voyez-vous un terme qui ne dépend pas de n (c’est-à-dire qui reste constant dans le temps) ?\n",
    "Comparez cela avec h[n], qui semble ne contenir que des termes décroissants.\n",
    "\n",
    "Hint: Essayez de séparer, dans s[n], les parties qui disparaissent au fil du temps de celle qui reste constante."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 2. Système causal"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Soit une famille de systèmes parametrée par $N$ avec les transformées en Z suivantes:\n",
    "\n",
    "$$ H_N(z) = \\frac{1}{2^N}\\sum_{k=0}^{2^N - 1} z^{-k} $$"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 1. Donner les réponses impulsionelles de ces systèmes."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 2. Calculez analytiquement les zéros de ces systèmes, pour toutes valeurs de N."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 3. Calculer numériquement pour $N \\in \\{1, 2, 3, 4\\}$ les zéros de ces systèmes et afficher les sur un diagramme pôles-zéros."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "zeros_list = []\n",
    "for i in range(0, 4):\n",
    "    ### Votre code ###\n",
    "    ##################"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def set_pz_plot(ax):\n",
    "\n",
    "     \"\"\"Helper function to draw unit circle and axes.\"\"\"\n",
    "     t = np.linspace(0, 2*np.pi, 100)\n",
    "     ax.plot(np.cos(t), np.sin(t), linewidth=1)\n",
    "     ax.vlines(0, ymin=-1.2, ymax=1.2)\n",
    "     ax.hlines(0, xmin=-1.2, xmax=1.2)\n",
    "     ax.set_xlabel(\"Réel\")\n",
    "     ax.set_ylabel(\"Imaginaire\")\n",
    "     ax.set_aspect('equal', 'box')\n",
    " \n",
    " \n",
    "def plot_pz_zeros(zeros_list):\n",
    "     \"\"\"Plot zeros for increasing N values.\"\"\"\n",
    "     fig, ax = plt.subplots(2, 2, figsize=(10, 10))\n",
    "     for i, zeros in enumerate(zeros_list):\n",
    "         row, col = divmod(i, 2)\n",
    "         set_pz_plot(ax[row, col])\n",
    "         ax[row, col].scatter(zeros.real, zeros.imag, marker='o', c='r', label='zéros')\n",
    "         ax[row, col].set_title(f\"Zéros pour $N={i+1}$\")\n",
    "     plt.tight_layout()\n",
    "\n",
    "     plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "plot_pz_zeros(zeros_list)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 4. Sur la base de la position des zéros uniquement, quel est le type de filtre qu'ils implémentent (passe-haut, passe-bas, passe-bande, coupe-bande)?"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 5. Affichez leurs réponses en fréquence (en amplitude et en phase, avec une échelle logarithmique pour le premier) pour les valeurs $N \\in \\{1, 2, 3, 4\\}$."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "##############\n",
    "# Votre code #\n",
    "##############"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def rb_log(h):\n",
    "     return np.log10(np.abs(h) + 1e-6)\n",
    "\n",
    "def plot_freq_response(w, h_full):\n",
    "\n",
    "    fig, ax = plt.subplots(2, 1, sharex=True, figsize=(10, 6))\n",
    "    ax[0].set_title('Réponse en fréquence')\n",
    "    colors = ['b', 'orange', 'g', 'r']\n",
    "\n",
    "    for i, color in enumerate(colors):\n",
    "        ax[0].plot(w/np.pi, 20 * rb_log(np.abs(h_full[i, :])), color=color, label=f\"N={i+1}\")\n",
    "        ax[0].grid(True)\n",
    "        ax[0].set_ylabel('Amplitude [dB]')\n",
    "        ax[0].set_xlabel('Fréquence [$\\\\pi$ rad/sample]')\n",
    " \n",
    "\n",
    "    for i, color in enumerate(colors):\n",
    "        ax[1].plot(w/np.pi, np.angle(h_full[i, :]), color=color, label=f\"N={i+1}\")\n",
    "        ax[1].grid(True)\n",
    "        ax[1].set_ylabel('Phase [radian]')\n",
    "        ax[1].set_xlabel('Fréquence [$\\\\pi$ rad/sample]')\n",
    " \n",
    "    plt.legend()\n",
    "    plt.tight_layout()\n",
    "    plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "plot_freq_response(w, h_full)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 6. Quel est l'effet de l'augmentation de $N$?"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Exercice 3: Étude d'un filtre\n",
    "\n",
    "Considérez le système illustré ci-dessous :\n",
    "\n",
    "\n",
    "![picture](images/Ex1_1.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 1. Déterminer la fonction de transfert du filtre et la mettre sous la forme suivante : \n",
    "\n",
    "$$\\begin{align}\n",
    "H(z) = \\frac{\\sum_{k=0}^K b_k z^{-k}}{\\sum_{k=0}^K a_k z^{-k}}\n",
    "\\end{align}\n",
    "$$"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 2. Quelle est l'équation différentielle régissant ce système ? (Indice : utiliser la fonction de transfert)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 3. Pour les paramètres $c_1 = \\frac{1}{8} , c_2 = \\frac{1}{16} $, calculer les pôles et les zéros du système. Le système est-il stable?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "##############\n",
    "# Votre code #\n",
    "##############\n",
    "\n",
    "\n",
    "print(\"Pôles:\\n{}\".format(\"\".join([\"\\t{:.4f}\\n\".format(p) for p in ps])))\n",
    "print(\"Zéros:\\n{}\".format(\"\".join([\"\\t{:.4f}\\n\".format(z) for z in zs])))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def plot_poles_zeros(p, z):\n",
    "    t = np.linspace(0, 2 * np.pi, 100)\n",
    "    plt.figure(figsize=(5, 10))\n",
    "    plt.plot(np.cos(t), np.sin(t), linewidth=1)\n",
    "    plt.vlines(0, ymin=-1.2, ymax=1.2)\n",
    "    plt.hlines(0, xmin=-1.2, xmax=1.2)\n",
    "    plt.scatter(p.real, p.imag, marker=\"x\", c=\"r\", label=\"pôles\")\n",
    "    plt.scatter(z.real, z.imag, marker=\"o\", c=\"r\", label=\"zéros\")\n",
    "    plt.legend()\n",
    "    plt.title(\"Pôles et zéros\")\n",
    "    plt.xlabel(\"Réel\")\n",
    "    plt.ylabel(\"Imaginaire\")\n",
    "\n",
    "\n",
    "plot_poles_zeros(ps, zs)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 4. Calculer et afficher la magnitude $|H(\\omega)|$ de la réponse fréquentielle du système. De quel type de filtre s'agit-t-il (passe-bas, passe-haut, passe-bande, coupe-bande, passe-tout) ?\n",
    "\n",
    "*Pour calculer $|H(\\omega)|$, créez une variable fréquentielle $\\omega$ dans l'intervalle $[-\\pi, \\pi]$ et tracez la magnitude $|H(z)|$ de la fonction de transfert que vous avez calculée question 2 pour les valeurs de $z$ dans le cercle unité ($z=e^{j \\omega}$).*"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Completez la fonction H #\n",
    "###########################\n",
    "def H(omega, a_coeffs, b_coeffs):\n",
    "    amplitude = None\n",
    "    phase = None\n",
    "    return amplitude, phase"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def plot_magnitude(amp, omega):\n",
    "    fig, ax = plt.subplots(figsize=(10, 6))\n",
    "    \n",
    "    ax.set_title('Réponse en fréquence de $H(z)$ (amp)')\n",
    "    ax.plot(omega / np.pi, amp, 'b')\n",
    "    ax.grid('on')\n",
    "    ax.set_ylabel('Amplitude')\n",
    "    ax.set_xlabel('Fréquence [$\\pi$ rad/sample]')\n",
    "    \n",
    "    plt.tight_layout()\n",
    "    plt.show()\n",
    "\n",
    "def plot_phase(phase, omega):\n",
    "    \n",
    "    fig, ax = plt.subplots(figsize=(10, 6))\n",
    "    \n",
    "    ax.set_title('Réponse en fréquence de $H(z)$ (phase)')\n",
    "    ax.plot(omega / np.pi, phase, 'g')\n",
    "    ax.grid('on')\n",
    "    ax.set_ylabel('Phase (radian)')\n",
    "    ax.set_xlabel('Fréquence [$\\pi$ rad/sample]')\n",
    "    plt.tight_layout()\n",
    "    plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Votre Code #\n",
    "##############\n",
    "\n",
    "\n",
    "amplitude, phase = H(omega, a_coeffs, b_coeffs)\n",
    "plot_magnitude(amplitude, omega)\n",
    "plot_phase(phase, omega)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Exercice 4 : Filtre du deuxième ordre"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Considérez le système suivant:\n",
    "\n",
    "$$ y[n] = -a_1y[n-1] - a_2 y[n-2] + b_0 x[n] + b_1x[n-1]$$\n",
    "\n",
    "#### 1. Écrivez la function de transfert $H(z)$."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "####  2. Suggérer une structure d'implémentation pour ce filtre en utilisant le nombre minimum d'éléments \"retard\".\n",
    "\n",
    "Pour afficher un dessin vous pouvez par exemple:\n",
    "  - Faire le diagramme en utilisant https://draw.io\n",
    "  - Exporter le dessing au format png, l'enregistrer dans le même dossier que le notebook\n",
    "  - insérer une cellule  en tapant le code suivant dans une cellule markdown"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 3. Pour les valeurs $a_1= 2.5, a_2= 1 , b_1 = 3 $, pour quelles valeurs de $b_0$ le filtre est-il stable?\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "####  4. Considérez maintenant les valeurs $a_2=0.25, b_0=2, b_1=3$. Faites varier $a_1$ dans l'intervalle $[-1.8, 1.7]$ avec un pas de 0.5, et cherchez les configurations dans lesquelles le filtre est stable. Pour les valeurs dans la zone de stabilité, tracez l'amplitude $|H(\\omega)|$ et la phase $\\angle H(\\omega)$ de la réponse en fréquence du spectre complet $H$. Discuter le comportement général du système (quelles fréquences le système laisse-t-il passer ?). "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "##############\n",
    "# Votre code #\n",
    "##############"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def plot_poles_zeros(poles_list, zeros_list, a1_values):\n",
    "    fig, axes = plt.subplots(2, 4, figsize=(30, 10))\n",
    "    plt.suptitle(\"Pôles et zéros\")\n",
    "    for a1, p, z, ax in zip(a1_values, poles_list, zeros_list, axes.flat):\n",
    "        t = np.linspace(0, 2*np.pi, 100)\n",
    "        ax.plot(np.cos(t), np.sin(t), linewidth=1)  # unit circle\n",
    "        ax.vlines(0, ymin=-1.2, ymax=1.2)\n",
    "        ax.hlines(0, xmin=-1.2, xmax=1.2)\n",
    "        ax.scatter(p.real, p.imag, marker='x', c='r', label='pôles')\n",
    "        ax.scatter(z.real, z.imag, marker='o', c='b', label='zéros')\n",
    "        ax.legend()\n",
    "        ax.set_title(f\"$a_1 = {a1}$\")\n",
    "        ax.set(xlabel=\"Réel\", ylabel=\"Imaginaire\")\n",
    "        ax.set_aspect('equal', 'box')\n",
    "\n",
    "    plt.tight_layout()\n",
    "    plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "plot_poles_zeros(poles_list, zeros_list, a1_values)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "##############\n",
    "# Completez le code #\n",
    "##############\n",
    "\n",
    "a_coeffs = None\n",
    "b_coeffs = None\n",
    "a1_values = None\n",
    "responses = []\n",
    "\n",
    "for a1 in a1_values:\n",
    "    \n",
    "    responses.append((a1, w, h))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def plot_freq_response_a1(responses):\n",
    "\n",
    "    fig, ax = plt.subplots(2, 1, figsize=(14, 14))\n",
    "    ax[0].set_title(\"Réponse en fréquence de $H(z)$\")\n",
    "\n",
    "    legends = []\n",
    "    for (a1, w, h) in responses:\n",
    "        ax[0].plot(w/np.pi, 20 * np.log10(np.abs(h) + 1e-8))  # Magnitude in dB\n",
    "        ax[1].plot(w/np.pi, np.angle(h))\n",
    "        legends.append(f\"a₁={a1:.2f}\")\n",
    "\n",
    "        ax[0].grid(True)\n",
    "        ax[0].set_ylabel(\"Amplitude [dB]\")\n",
    "        ax[0].set_xlabel(\"Fréquence [$\\\\pi$ rad/sample]\")\n",
    "        ax[0].legend(legends)\n",
    "\n",
    "        ax[1].grid(True)\n",
    "        ax[1].set_ylabel(\"Phase (radian)\")\n",
    "        ax[1].set_xlabel(\"Fréquence [$\\\\pi$ rad/sample]\")\n",
    "        ax[1].legend(legends)\n",
    "\n",
    "    plt.tight_layout()\n",
    "    plt.show()\n",
    "\n",
    "plot_freq_response_a1(responses)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": ".venv",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
