{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "40b53952",
   "metadata": {},
   "source": [
    "# Traitement du Signal - TDM5 : Transformée de Fourier Réelle\n",
    "\n",
    "---\n",
    "\n",
    "Aujourd'hui, place à la transformée de Fourier Réelle. \n",
    "\n",
    "*\"Hmmm, ça me rappelle quelque chose...\"*\n",
    "\n",
    "On va voir étape par étape comment faire une bonne transformée de Fourier réelle, sa transformée inverse, comment bien afficher les résultats. Donc prenez le temps de bien comprendre, cela ne va pas être très technique.\n",
    "\n",
    "*\"... Y'a un bug, non ?\"*"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c73687d5",
   "metadata": {},
   "outputs": [],
   "source": [
    "# A COMPLETER\n",
    "# Chargement des librairies\n",
    "..."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "73b4a218",
   "metadata": {},
   "source": [
    "## Exercice 1 : Comment on fait une bonne transformée de Fourier réelle ?\n",
    "\n",
    "*\"Mais j'ai ouvert le fichier de la semaine dernière ou quoi ?\"*\n",
    "\n",
    "On commence tranquillement, avec un signal simple : une sinusoïde. \n",
    "\n",
    "\\begin{equation*}\n",
    "    s(t) = A  \\sin(2 \\pi f_{0} t)\n",
    "\\end{equation*}\n",
    "\n",
    "Avec $A = 1$ et $f_{0} = 50 Hz$.\n",
    "\n",
    "Le signal évoluera de 0 à 0.1 secondes avec une fréquence d'échantillonage $f_e = 1000 Hz$.\n",
    "\n",
    "Construisez l'axe temporel, le signal sinusoïdal et tracez tout ça.\n",
    "\n",
    "*\"Mais c'est le même signal que la semaine dernière ?!\"*"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "13781482",
   "metadata": {},
   "outputs": [],
   "source": [
    "# A COMPLETER\n",
    "# Création de l'axe temporel\n",
    "...\n",
    "\n",
    "# Création du signal sinuisoïdal\n",
    "...\n",
    "\n",
    "# Afficage du signal\n",
    "..."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c60dba54",
   "metadata": {},
   "source": [
    "Oui, on reprend le même signal, car il conviendra très bien pour notre développement. Comme vous avez pu le constater la semaine dernière, le spectre d'amplitude était pair, et le spectre de phase impair. C'est normal, on a utilisé un signal réel.\n",
    "\n",
    "De ce fait, on pourrait se passer de la moitié des informations affichées, ainsi que quelques étapes relou. Et c'est là qu'intervient la transformée de Fourier réelle, une variante de la transformée de Fourier, qui est généralement plus simple à prendre en main.\n",
    "\n",
    "*\"Mais pourquoi on a passé du temps la semaine dernière sur l'autre transformée ?\"*\n",
    "\n",
    "Pour 2 raisons principalement:\n",
    "- Il n'est pas garanti que le signal d'entrée soit réel. C'est très souvent le cas, mais au-cas-où, c'est toujours mieux de savoir manier l'original\n",
    "- Nous verrons plus tard comment calculer à la main la transformée de Fourier classique. La transformée réelle étant une variante, elle demande des calculs supplémentaires bien ch**** à réaliser, donc on se contentera de faire uniquement l'originale ;)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e30d4516",
   "metadata": {},
   "source": [
    "Sur la plateforme web, construisez également ce signal, et analysez les spectres d'amplitude et de phase de la transformée **réelle** obtenue\n",
    "\n",
    "*Je le répète, pour être sûr que vous ne vous trompez pas, **_TRANSFORMEE REELLE_***\n",
    "\n",
    "Répondez maintenant aux questions suivantes:"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3dcfe50f",
   "metadata": {},
   "source": [
    "**_QUESTION :_** Sur le spectre d'amplitude, que donne la transformée de Fourier réelle ?\n",
    "\n",
    "*Note : précisez bien les **_fréquences_** et **_amplitudes_**, c'est important dans l'analyse*"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d5f178aa",
   "metadata": {},
   "source": [
    "**_REPONSE :_**"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9a85e45f",
   "metadata": {},
   "source": [
    "**_QUESTION :_** Quelle est la range du spectre d'amplitude ?\n",
    "\n",
    "*(C'est-à-dire les valeurs min et max que la phase peut prendre)*"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "dae4c568",
   "metadata": {},
   "source": [
    "**_REPONSE :_**"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7b03465f",
   "metadata": {},
   "source": [
    "**_QUESTION :_** Sur les deux spectres, quelles sont les fréquences min et max sur l'axe ?\n",
    "\n",
    "*Oui, cette range n'est pas affichée au hasard, mais de la même manière que le signal temporel est défini dans un intervalle de temps, le signal fréquentiel est défini dans une range de fréquences*"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "61aece69",
   "metadata": {},
   "source": [
    "**_REPONSE :_**"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9c064cbd",
   "metadata": {},
   "source": [
    "Voilà, vous avez tous les critères primordiaux à vérifier afin de savoir si votre transformée de Fourier réelle est correcte:\n",
    "\n",
    "- L'allure du spectre d'amplitude\n",
    "- L'amplitude du pic dans le spectre d'amplitude\n",
    "- La fréquence du pic (la position sur l'axe des abscisses)\n",
    "- La range min/max de la phase\n",
    "- La range des fréquences affichées (min/max sur l'axe des abscisses)\n",
    "\n",
    "Si un seul de ces points n'est pas correct, ça veut dire **que votre transformée de Fourier réelle n'est pas correcte**. Donc, à vous vérifier chacun des points précédents dans la suite de l'exercice (et plus généralement lorsque vous calculez une transformée de Fourier réelle)."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d835a70f",
   "metadata": {},
   "source": [
    "La transformée de Fourier est réalisable avec la fonction NumPy *np.fft.fft*. Pour la transformée de Fourier réelle, il faut utiliser une autre fonction Numpy."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d9d31fe7",
   "metadata": {},
   "source": [
    "**_QUESTION :_** Quelle fonction NumPy permet de réaliser une transformée de Fourier réelle ? Quelles sont les paramètres d'entrée et que renvoit elle en sortie ?"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7231ee56",
   "metadata": {},
   "source": [
    "**_REPONSE :_**"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7c05fa15",
   "metadata": {},
   "source": [
    "On va accélérer par rapport à la semaine dernière. Ici,\n",
    "- Calculez la transformée de Fourier réelle $S(f)$ du signal $s(t)$\n",
    "- Calculez et affichez les spectres d'amplitude et de phase de $S(f)$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "027e41de",
   "metadata": {},
   "outputs": [],
   "source": [
    "# A COMPLETER\n",
    "# Calcul de la transformée de Fourier réelle du signal et affichage du spectre d'amplitude et de phase\n",
    "..."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4d8d1ca6",
   "metadata": {},
   "source": [
    "**_QUESTION :_** Par rapport à tous les précédents critères à valider, que vous avez identifié, lesquels sont respectés ? Lesquels ne sont pas validés ?"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7d98d96b",
   "metadata": {},
   "source": [
    "**_REPONSE :_**"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cd40532d",
   "metadata": {},
   "source": [
    "Ok, vous avez donc des corrections à apporter sur ces spectres. Précisément 2 corrections.\n",
    "\n",
    "La première, c'est l'axe fréquentiel absent. Il faut le calculer, également avec une fonction NumPy. Pour la transformée classique, on utilise *np.fft.freq*, mais pour la transformée réelle, il vous faut une autre fonction"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7a688284",
   "metadata": {},
   "source": [
    "**_QUESTION :_** Quelle fonction NumPy permet de calculer l'axe fréquentiel de la transformée de Fourier réelle ? Quels sont les paramètres d'entrée, et que renvoit la fonction en sortie ?"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "61d60227",
   "metadata": {},
   "source": [
    "**_REPONSE :_**"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4ab0e6bb",
   "metadata": {},
   "source": [
    "Grâce à votre nouvelle fonction, calculez l'axe fréquentiel de la transformée de Fourier réelle, et tracez les spectres d'amplitude et de phase corrigés"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c70161bf",
   "metadata": {},
   "outputs": [],
   "source": [
    "# A COMPLETER\n",
    "# Calcul de l'axe fréquentiel de la transformée de Fourier réelle et affichage du spectre d'amplitude et de phase\n",
    "..."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "31aa20c6",
   "metadata": {},
   "source": [
    "**_QUESTION :_** Par rapport à tous les précédents critères à valider, que vous avez identifié, lesquels sont respectés ? Lesquels ne sont pas validés ?"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "de514acd",
   "metadata": {},
   "source": [
    "**_REPONSE :_**"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "42d6f7da",
   "metadata": {},
   "source": [
    "La 2ème, et dernière correction, concerne l'amplitude du spectre à normaliser. Pour la transformée de Fourier, je vous avais indiqué de diviser le spectre par le temps d'évolution du signal ($T = 0.1 s$), multiplié par la fréquence d'échantillonnage ($f_e = 1000 Hz$). \n",
    "\n",
    "Pour la transformée réelle, c'est légèrement différent, puisqu'on a un repliement du spectre des fréquences négatives dans le positif.\n",
    "\n",
    "*\"Ca veut dire quoi ce charabia ?\"*\n",
    "\n",
    "Ce n'est pas très compliqué. Ca signifie ici que le spectre d'amplitude réel à $f_0$ montre la somme des amplitudes à $f_0$ et $-f_0$ du spectre d'amplitude de la transformée classique. Etant donné que le signal est réel, le spectre d'amplitude est pair, et donc, **les amplitudes affichées sont 2 fois supérieures que sur la transformée classique**.\n",
    "\n",
    "De ce fait, la normalisation à effectuer est la suivante: \n",
    "\n",
    "\\begin{equation*}\n",
    "    A_{norm}(f) = \\frac{A(f) \\times 2}{T \\times f_e}\n",
    "\\end{equation*}\n",
    "\n",
    "Avec $A(f)$ le spectre d'amplitude, $A_{norm}(f)$ le spectre d'amplitude normalisé, $T$ la durée du signal en temporel (en secondes) et $f_e$ la fréquence d'échantillonnage.\n",
    "\n",
    "Cependant, il y a 2 endroits où il ne faut pas multiplier par 2:\n",
    "- à $0 Hz$\n",
    "- à $\\frac{f_e}{2} Hz$ ssi $N$, le nombre d'échantillons (la taille du spectre fréquentiel), est pair\n",
    "\n",
    "*\"Ok, mais pourquoi ?\"*\n",
    "\n",
    "Ca, on verra ça plus tard au moment d'étudier la transformée de Fourier discrète (dans quelques semaines)...\n",
    "\n",
    "Normalisez le spectre d'amplitude et affichez les spectres"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d59472fb",
   "metadata": {},
   "outputs": [],
   "source": [
    "# A COMPLETER\n",
    "# Normalisation du spectre d'amplitude et affichage du spectre d'amplitude et de phase\n",
    "..."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1e6c79b6",
   "metadata": {},
   "source": [
    "**_QUESTION :_** Par rapport à tous les précédents critères à valider, que vous avez identifié, lesquels sont respectés ? Lesquels ne sont pas validés ?"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "242cba0f",
   "metadata": {},
   "source": [
    "**_REPONSE :_**"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0ef50981",
   "metadata": {},
   "source": [
    "Bon, bah ça s'est bien passé ! Il y a moins d'étapes que pour la FFT classique, mais ça n'empêche qu'il ne faut pas oublier quelques détails importants.\n",
    "\n",
    "Comme la semaine dernière, on pourrait également appliquer un filtrage sur le spectre de phase pour montrer les valeurs significatives (c-a-d celles où l'amplitude est non nulle). Mais bon, ce n'est pas primordial, donc on va se contenter de ce qu'on a déjà ;)\n",
    "\n",
    "Donc, comme vous avez l'habitude maintenant, complétez la golden cellule ci-dessous afin d'effectuer une transformée de Fourier réelle correcte à partir d'un signal, de sa durée $T$ et de sa fréquence d'échantillonnage $f_e$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "be646a8c",
   "metadata": {},
   "outputs": [],
   "source": [
    "# GOLDEN CELLULE A COMPLETER\n",
    "# Transformée de Fourier réelle d'un signal et affichage des spectres d'amplitude et de phase\n",
    "\n",
    "# Paramètres (MODIFIEZ AVEC VOS NOMS DE VARIABLES)\n",
    "# ... : un signal réel temporel NumPy\n",
    "# ... : la durée du signal \n",
    "# ... : la fréquence d'échantillonnage\n",
    "\n",
    "..."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5895474e",
   "metadata": {},
   "source": [
    "Ok, la transformée réelle, c'est fait. Passons à la transformée réelle inverse. Et pour cela, on reprend le signal fréquentiel de la semaine dernière"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6de1d190",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Création du signal X(f) et de l'axe fréquentiel\n",
    "fe = 2000  # Fréquence d'échantillonnage\n",
    "N = 100\n",
    "\n",
    "df = fe / N\n",
    "\n",
    "# Axe fréquentiel de la RFFT : de 0 à fe/2\n",
    "freq_X = np.arange(N//2 + 1) * df\n",
    "\n",
    "# Construction du spectre X(f)\n",
    "X = np.zeros(N//2 + 1, dtype=complex)\n",
    "\n",
    "X[freq_X == 100] = 250\n",
    "\n",
    "plt.figure()\n",
    "\n",
    "plt.subplot(2,1,1)\n",
    "plt.plot(freq_X, np.abs(X) * 2 / N)\n",
    "plt.xticks(np.arange(0, fe/2 + 1, 200))\n",
    "plt.title(\"Spectre d'amplitude\")\n",
    "plt.xlabel(\"Fréquence\")\n",
    "plt.ylabel(\"Amplitude\")\n",
    "\n",
    "plt.subplot(2,1,2)\n",
    "plt.plot(freq_X, np.angle(X))\n",
    "plt.xticks(np.arange(0, fe/2 + 1, 200))\n",
    "plt.title(\"Spectre de phase\")\n",
    "plt.xlabel(\"Fréquence\")\n",
    "plt.ylabel(\"Phase\")\n",
    "\n",
    "plt.tight_layout()\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b9f635e0",
   "metadata": {},
   "source": [
    "On va maintenant, comme lors du précédent TDM, calculer la transformée de Fourier inverse. On avait pour cela utilisé *np.fft.ifft*, mais petit rappel, on a un signal fréquentiel issu d'une transformée de Fourier réelle, donc on doit utiliser une autre fonction."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e0c81a72",
   "metadata": {},
   "source": [
    "**_QUESTION :_** Quelle est la fonction NumPy à utiliser pour calculer la transformée de Fourier réelle inverse ? Quels paramètres prend elle en entrée, et que retourne elle en sortie ?"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d6f85350",
   "metadata": {},
   "source": [
    "**_REPONSE :_**"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1eb35332",
   "metadata": {},
   "source": [
    "A vous de jouer, calculez la transformée de Fourier inverse réelle de $X(f)$. 2 infos:\n",
    "- Comme pour la transformée inverse vue la semaine dernière, il faut également calculer l'axe temporel à la main, car il n'y a pas de fonction pour cela. \n",
    "- En revanche, étant donné que la transformée de Fourier réelle utilise uniquement des signaux réels, le signal en sortie de la transformée de Fourier réelle inverse est déjà un signal temporel réel, donc on peut directement l'afficher sans post-traitement supplémentaire."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2faf4ba3",
   "metadata": {},
   "outputs": [],
   "source": [
    "# GOLDEN CELLULE A COMPLETER\n",
    "# Transformée de Fourier réelle inverse d'un signal fréquentiel et affichage du signal temporel\n",
    "\n",
    "# Paramètres (MODIFIEZ AVEC VOS NOMS DE VARIABLES)\n",
    "# ... : un signal fréquentiel NumPy\n",
    "# ... : la fréquence d'échantillonnage\n",
    "\n",
    "..."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d1a963b6",
   "metadata": {},
   "source": [
    "**_QUESTION :_** Est-ce que le résultat correspond à ce que vous attendiez ?"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "46d74b00",
   "metadata": {},
   "source": [
    "**_REPONSE :_**"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "923fb18b",
   "metadata": {},
   "source": [
    "---\n",
    "\n",
    "## Exercice 2 : Comment compresser un signal ?\n",
    "\n",
    "Dans cet exercice, on va voir comment on peut **compresser** théoriquement un signal, grâce à sa représentation fréquentielle. Pour cela, il nous faut un signal. Tracez le signal **périodique** carré de période $T$ et d'amplitude $A$, dont l'équation de la période est la suivante : \n",
    "\n",
    "\\begin{equation*}\n",
    "    c(t) = \\left \\{\n",
    "            \\begin{array}{ll}\n",
    "                A & 0 \\leq t \\leq \\frac{T}{2}\\\\\n",
    "                -A & \\frac{T}{2} \\leq t \\leq T \\\\\n",
    "            \\end{array}\n",
    "            \\right.\n",
    "\\end{equation*}\n",
    "\n",
    "Le signal évoluera de 0 à 1 secondes avec une fréquence d'échantillonage $f_e = 10000$ Hz. La période $T$ est de 0.1 secondes, et l'amplitude $A$ est à 1.\n",
    "\n",
    "Créez et tracez ce signal."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "76d71b3e",
   "metadata": {},
   "outputs": [],
   "source": [
    "# A COMPLETER\n",
    "# Création d'un signal carré et affichage du signal temporel\n",
    "..."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b7691bfe",
   "metadata": {},
   "source": [
    "Effectuez maintenant une transformée de Fourier réelle sur le signal carré, et affichez le spectre d'amplitude."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f2711977",
   "metadata": {},
   "outputs": [],
   "source": [
    "# A COMPLETER\n",
    "# Transformée de Fourier réelle du signal c(t) et affichage du spectre d'amplitude de C(f)\n",
    "..."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6bf06000",
   "metadata": {},
   "source": [
    "Identifiez le nombre de composantes du signal fréquentiel, c'est-à-dire, le nombre de pics supérieurs à 1% de l'amplitude maximum sur le spectre d'amplitude\n",
    "\n",
    "*Si vous voulez, vous pouvez utiliser la fonction scipy.signal.find_peaks, mais les fonctions NumPy usuelles que vous connaissez font largement l'affaire*"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "58bcc885",
   "metadata": {},
   "outputs": [],
   "source": [
    "# A COMPLETER\n",
    "# Identification du nombre de composantes du signal fréquentiel C(f)\n",
    "..."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0d4378da",
   "metadata": {},
   "source": [
    "L'objectif de la compression ici va être de simplifier le signal en ne gardant que les plus grandes amplitudes du signal. Il y'a 2 manières de réaliser cela:\n",
    "- Ne gardez que les $K$ fréquences avec les plus grandes amplitudes\n",
    "- Ne gardez que les fréquences dont l'amplitude est supérieure à un seuil défini\n",
    "\n",
    "Les fréquences significatives sont donc conservées, et les autres sont mises à zéro."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2324d450",
   "metadata": {},
   "source": [
    "Dans cet exercice, nous allons implémenter la première méthode. Il faut donc identifier les $K$ composantes les plus importantes, donc les $K$ composantes avec les plus grandes amplitudes.\n",
    "\n",
    "La première étape consiste donc à identifier les $K$ plus grandes amplitudes. Pour cela, on va devoir faire un tri de tableau, qu'on peut faire avec *np.sort*. L'idée ici va être de récupérer la $k$-ième plus grande amplitude. \n",
    "\n",
    "Avec $K=10$, récupérez la valeur de la $K$-ième plus grande amplitude.\n",
    "\n",
    "*Au-cas-où, si ce n'est pas clair, il faut ici récupérer la 10ème plus grande valeur du vecteur du spectre d'amplitude*"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "862ca0a1",
   "metadata": {},
   "outputs": [],
   "source": [
    "# A COMPLETER\n",
    "# Récupération de la K-ième plus grande amplitude\n",
    "K = 10\n",
    "\n",
    "..."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e942a21e",
   "metadata": {},
   "source": [
    "Cette valeur obtenue va nous servir de seuil: toutes les valeurs du signal fréquentiel dont l'amplitude est inférieure à ce seuil seront mises à zéro. Vous pouvez le faire avec une modification conditionnelle (*np.where*). \n",
    "\n",
    "A vous de jouer, calculez ce nouveau signal fréquentiel, et affichez le spectre d'amplitude du résultat"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "79e173c3",
   "metadata": {},
   "outputs": [],
   "source": [
    "# A COMPLETER\n",
    "# Modification du signal fréquentiel pour ne conserver que les K plus grandes amplitudes et affichage du spectre d'amplitude modifié\n",
    "..."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c70e9de9",
   "metadata": {},
   "source": [
    "Normalement, si tout est bien fait, vous avez beaucoup moins de pics. On va s'assurer que vous avez uniquement 10 pics, en affichant le spectre, mais uniquement sur les 500 premières fréquences (ou si vous préférez, les 500 premières valeurs des vecteurs)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "9ca97987",
   "metadata": {},
   "outputs": [],
   "source": [
    "# A COMPLETER\n",
    "# Affichage du spectre d'amplitude modifié zoomé sur les 500 premières fréquences\n",
    "..."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "86006262",
   "metadata": {},
   "source": [
    "Vous avez compté ? Vous avez bien 10 pics ? Si c'est le cas, avant de passer à la suite, on va regrouper le code précédent, pour créer une fonction qui, à partir d'un signal fréquentiel et d'un nombre de composantes $K$, renvoit le signal fréquentiel avec uniquement les $K$ composantes les plus importantes. Pourquoi ? Parce qu'on va utiliser la fonction un bon nombre de fois dans la suite du TDM"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "782f377e",
   "metadata": {},
   "outputs": [],
   "source": [
    "# A COMPLETER\n",
    "# Fonction de récupération des K plus grandes amplitudes d'un signal fréquentiel et affichage du spectre d'amplitude modifié\n",
    "..."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8f218e3a",
   "metadata": {},
   "source": [
    "Regardons maintenant comment se comporte notre nouveau signal avec uniquement les 10 composantes les plus significatives. Effectuez une transformée de Fourier réelle inverse du signal fréquentiel modifié, et affichez le signal temporel avec l'original sur une même figure pour pouvoir le comparer"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ceae54b0",
   "metadata": {},
   "outputs": [],
   "source": [
    "# A COMPLETER\n",
    "# Transformée de Fourier réelle inverse du signal C(f) et affichage du signal temporel c(t)\n",
    "..."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6f7b9051",
   "metadata": {},
   "source": [
    "Afin de mesurer la ressemblance entre le signal original et le signal compressé, on va calculer le **rapport signal sur bruit**. Pour rappel, cette métrique indique la puissance du signal par rapport à la puissance de son bruit. Plus le rapport est grand, plus le signal est puissant par rapport à son bruit, et donc plus le bruit est faible. Pour cela, nous allons utiliser la formule suivante :\n",
    "\n",
    "\\begin{equation*}\n",
    "    R_{S/B} = 10*log_{10}(\\frac{\\sum s[n]²}{\\sum b[n]²})\n",
    "\\end{equation*}\n",
    "\n",
    "Ici, $s$ correspond au signal et $b$ le bruit. La mesure se calcule en décibels (dB), afin d'utiliser une échelle logarithmique plus lisible :\n",
    "- *0 dB* : Le signal et l'erreur ont la même puissance, donc le bruit prédomine sur le signal\n",
    "- *> 10 dB* : Le signal domine clairement le bruit\n",
    "- *20-30 dB* : On a une bonne qualité de reconstruction\n",
    "- *40 dB* : Le signal bruité est quasi indiscernable de l'original.\n",
    "\n",
    "Le calcul n'est pas compliqué, mais pour être sûr que le calcul soit correct (et parce qu'il y a quelques légères subtilités dans le calcul), vous pouvez utiliser la fonction suivante"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cd19c0b0",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Fonction de calcul du rapport S/B\n",
    "def rapport_sb(signal,bruit):\n",
    "    return 10 * np.log10(np.sum(signal.astype(float)**2) / np.sum(bruit.astype(float)**2))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "906c7930",
   "metadata": {},
   "source": [
    "*\"Ok, mais ici, c'est quoi le bruit ?\"*\n",
    "\n",
    "Ici, on va considérer le bruit comme étant la différence entre le signal original et le signal compressé. De ce fait, plus le rapport est grand, plus la puissance du signal est grande par rapport à la puissance de la différence entre les deux signaux, et donc plus le signal compressé est proche du signal original.\n",
    "\n",
    "Calculez et affichez le rapport signal sur bruit entre le signal original et le signal compressé"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "909c6e0c",
   "metadata": {},
   "outputs": [],
   "source": [
    "# A COMPLETER\n",
    "# Calcul du rapport S/B entre le signal original et le signal reconstruit\n",
    "..."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "39f90356",
   "metadata": {},
   "source": [
    "Répétez les mêmes opérations pour avoir sur la même figure le signal original et plusieurs versions compressées du signal ($K = 1, 5, 20, 100$). Calculez et affichez également le rapport signal sur bruit pour chacune des versions compressées du signal"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "75f43b1a",
   "metadata": {},
   "outputs": [],
   "source": [
    "# A COMPLETER\n",
    "# Affichage du signal original et de ses versions compressées, et calcul du rapport S/B pour différentes valeurs de K\n",
    "..."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ca8ad74b",
   "metadata": {},
   "source": [
    "**_QUESTION :_** A partir de combien de composantes $K$ a-t-on une compression indiscernable du signal original ?\n",
    "\n",
    "*La première question est de savoir ce qu'on considère comme indiscernable*"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "aa91270c",
   "metadata": {},
   "source": [
    "**_REPONSE :_**"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "44409db4",
   "metadata": {},
   "source": [
    "---\n",
    "\n",
    "## Exercice 3 : Mon audio est beaucoup trop lourd...\n",
    "\n",
    "J'ai trop peu d'espace sur mon ordi, au point de ne plus avoir la place pour mon nouveau son. Vous pouvez m'aider ?"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bbce03f4",
   "metadata": {},
   "source": [
    "Chargez l'audio *music.wav* et tracez le signal."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5e504ba2",
   "metadata": {},
   "outputs": [],
   "source": [
    "# A COMPLETER\n",
    "# Chargement et affichage de music.wav\n",
    "..."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bc4ee931",
   "metadata": {},
   "source": [
    "Effectuez une transformée de Fourier réelle sur le signal audio et affichez le spectre d'amplitude du résultat obtenu.\n",
    "\n",
    "*Bien évidemment, je vous invite à faire de même sur la plateforme web, afin de vérifier que vous avez **_le même résultat_***"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "af6fea4c",
   "metadata": {},
   "outputs": [],
   "source": [
    "# A COMPLETER\n",
    "# Calcul de la transformée de Fourier réelle du signal music.wav et affichage du spectre d'amplitude\n",
    "..."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ea6f31e2",
   "metadata": {},
   "source": [
    "Comme vous vous en doûtez, on va ici compresser le signal en gardant les $K$ composantes les plus importantes. Au lieu de tester toutes les valeurs de $K$, on va uniquement sélectionner les valeurs tel que le rapport S/B entre le signal original et le signal compressé reconstruit est à 10, 20, 30 et 40 dB. On pourra ensuite exporter les audios et écouter le résultat obtenu.\n",
    "\n",
    "Pour vous aider dans ce processus, il faut déterminer les valeurs de $K$ permettant d'obtenir approximativement ce rapport S/B. Pour cela, partez avec $K$ comme étant des puissances de 2, afin de considérablement réduire le champ des possibles. Avec ces valeurs de $K$, effectuez la compression, reconstruisez le signal temporel et calculez les rapports S/B. Enfin, vous aurez juste à prendre les signaux reconstruits dont le rapport S/B est supérieur et le plus proche des différents seuils visés (10, 20, 30 et 40 dB).\n",
    "\n",
    "*Pour l'enregistrement d'un audio, utilisez la fonction wavfile.write (dans le package scipy.io), en modifiant le type de données de votre signal audio :*\n",
    "\n",
    "`wavfile.write(\"mon_audio.wav\", frequence_echantillonnage_music, signal_music.astype(np.int16))`\n",
    "\n",
    "*Avec `frequence_echantillonnage_music` la fréquence d'échantillonnage du signal, et `signal_music` le signal audio temporel (vecteur NumPy)*"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f391b40e",
   "metadata": {},
   "outputs": [],
   "source": [
    "# A COMPLETER\n",
    "# Compression du signal audio tel que le rapport S/B soit supérieur à 10/20/30/40 dB\n",
    "..."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b2d24dba",
   "metadata": {},
   "source": [
    "**_QUESTION :_** Comment est la qualité sonore des différents extraits compressés ?"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "578ffac2",
   "metadata": {},
   "source": [
    "**_REPONSE :_**"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "99fee9db",
   "metadata": {},
   "source": [
    "**_QUESTION :_** Est-ce que les fichiers compressés ont un poids plus léger (en octets) ?"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "96023335",
   "metadata": {},
   "source": [
    "**_REPONSE :_**"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d03e7e32",
   "metadata": {},
   "source": [
    "---\n",
    "\n",
    "*Le résultat n'est pas concluant ? C'est normal !*\n",
    "\n",
    "La compression audio ne fonctionne pas exactement comme ça... Le principe reste le même : éliminer des fréquences inutiles en les mettant à zéro. Cependant, c'est fait de manière plus intelligente :\n",
    "- On supprime les fréquences audios inperceptibles par l'oreille humaine\n",
    "- On supprime les fréquences inaudibles dans le contexte (ex: un son fort à 1kHz rend inaudible un son plus faible à 1,05 kHz)\n",
    "\n",
    "Ensuite, les autres fréquences ne sont pas laissées telles qu'elles, mais elles sont quantifiées. Cela consiste à donner moins de précision sur les fréquences à faible amplitude tout en conservant une grande précision pour les fréquences à forte amplitude. De ce fait, on réduit drastiquement la taille requise pour encoder le signal audio, et donc on peut compresser efficacement un fichier.\n",
    "\n",
    "Bref, votre expérience n'a pas fonctionné, mais ça nous a permis de voir dans les grandes lignes la base de la compression MP3 !"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "deep",
   "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.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
