{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Ce TP est dédié à l'acquisition d'une capacité numérique: Mettre en oeuvre différentes méthodes numérique afin d'apprendre à simuler le comportement d'un oscillateur. \n",
    "\n",
    "Vous allez effectuer vos simulations à l'aide du langage de programmation python dans le Notebook ci-dessous.\n",
    "\n",
    "# Importations des outils\n",
    "\n",
    "**QUESTION 1**\n",
    "\n",
    "Commençons par importer les librairies numpy avec le préfixe np ainsi que la librairie matplotlib.pyplot avec le préfixe plt"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Les oscillateurs que nous avons étudiés sont constitués de deux blocs en cascade. Pour l'oscillateur quasi-sinusoïdal ce sont les blocs amplificateur et filtre passe-bande du second ordre, Pour l'oscillateur de relaxation ce sont les blocs comparateur à hystérésis et intégrateur.\n",
    "\n",
    "Nous allons donc commencer par simuler le fonctionnement de chaque bloc indépendamment.\n",
    "\n",
    "# Blocs de l'oscillateur quasi-sinusoïdal\n",
    "\n",
    "## Bloc amplificateur\n",
    "\n",
    "Le bloc amplificateur est caractérisé par deux éléments :\n",
    "- son gain $A$ qui est le rapport entre les tensions de sortie et d'entrée en fonctionnement linéaire, soit $v_s = A\\times v_e$\n",
    "- ses tensions de saturation $V_{sat,+}$ et $V_{sat,-}$ qui sont les tensions limites de sa sortie\n",
    "\n",
    "**QUESTION 2**\n",
    "\n",
    "Définir une fonction\n",
    "\n",
    "*def sortie_amplificateur(v_e:float, V_sat_p:float, V_sat_m:float, A:float) -> v_s:float :*\n",
    "\n",
    "qui prends en argument les nombres :\n",
    "- *v_e*, tension à l'entrée de l'amplificateur $v_e$\n",
    "- *V_sat_p*, tension de saturation positive de l'amplificateur $V_{sat,+}$\n",
    "- *V_sat_m*, tension de saturation négative de l'amplificateur $V_{sat,-}$\n",
    "- *A*, le gain de l'amplificateur $A$\n",
    "\n",
    "et qui renvoie le nombre :\n",
    "- *v_s*, tension à la sortie de l'amplificateur $v_s$\n",
    "\n",
    "Nous ferons attention à distinguer les cas de saturation et de régime linéaire pour écrire notre fonction."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Testons notre fonction *sortie_amplificateur*\n",
    "\n",
    "**QUESTION 3**\n",
    "\n",
    "Définissez des variables et assignez des valeurs pour les paramètres :\n",
    "- *V_sat_p* tension de saturation positive (par exemple 15 V)\n",
    "- *V_sat_m* tension de saturation négative (par exemple -15 V)\n",
    "- *A* gain de l'amplificateur (par exemple 3)\n",
    "- *N* nombre de points calculés pour le test (par exemple 100)\n",
    "\n",
    "(N'hésitez pas à tester d'autres valeurs plus proche de votre TP2 ou pour en voir l'effet)\n",
    "\n",
    "Définissez des valeurs de tensions d'entrée à tester avec le np.array *entrées* qui sera une liste de N valeurs allant de *V_sat_m* à *V_sat_p*. On pourra utiliser pour cela la fonction :\n",
    "- *np.linspace(start,stop,num=50)* renvoie un np.array de 50 valeurs espacées équitablement dans l'intervalle [start,stop]\n",
    "\n",
    "Définissez le np.array *sorties* qui sera une liste de N zéros dans laquelle on stockera les résultats de notre fonction *sortie_amplificateur* pour chaque élément d'*entrées*. On pourra utiliser pour cela la fonction :\n",
    "- *np.zeros(shape)* renvoie un np.array de *shape* zéros\n",
    "\n",
    "A l'aide d'une boucle *for* assignez à chaque éléments *sorties[i]* la valeur renvoyé par votre fonction *sortie_amplificateur* prenant en arguments les paramètres précédents *V_sat_p*, *V_sat_m*, *A* et la tension d'entrée *entrées[i]*\n",
    "\n",
    "Affichez avec un *print()* le np.array *sorties* obtenu."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Affichons les résultats de notre test à l'aide d'un graphique afin de mieux contrôler visuellement le bon fonctionnement de notre fonction.\n",
    "\n",
    "**QUESTION 4**\n",
    "\n",
    "Tracez le np.array *sorties* en fonction du np.array *entrées* sous forme de point. On pourra utiliser les fonctions :\n",
    "- *plt.plot(x,y,'.')* trace y en fonction de x avec des points\n",
    "- *plt.xlabel(\"abscisses\")* affiche la chaîne de caractères *abscisses* sous l'axe des abscisses\n",
    "- *plt.ylabel(\"ordonnées\")* affiche la chaîne de caractères *ordonnées* à côté de l'axe des ordonnées\n",
    "- *plt.legend([\"légende\"])* affiche la chaîne de caractères *légende* en légende\n",
    "- *plt.show()* affiche le graphique prè-définit\n",
    "- *plt.close()* implique que les prochains tracés se feront sur un autre graphique"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Pour la suite de la simulation, nous aurons aussi besoin de calculer la dérivée de la tension de sortie de l'amplificateur $\\dfrac{\\mathrm{d}v_s}{\\mathrm{d}t}$ en fonction de la tension d'entrée de l'amplificateur $v_e$ et de sa dérivée $\\dfrac{\\mathrm{d}v_e}{\\mathrm{d}t}$.\n",
    "\n",
    "Pour cela nous distinguerons deux régimes :\n",
    "- soit l'amplificateur est en régime linéaire et $\\dfrac{\\mathrm{d}v_s}{\\mathrm{d}t} = A\\times\\dfrac{\\mathrm{d}v_e}{\\mathrm{d}t}$\n",
    "- soit l'amplificateur est en régime saturé et la sortie est constante égale à sa tension de saturation, donc de dérivée nulle $\\dfrac{\\mathrm{d}v_s}{\\mathrm{d}t} = 0$\n",
    "\n",
    "**QUESTION 5**\n",
    "\n",
    "Re-Définir la fonction\n",
    "\n",
    "*def sortie_amplificateur(v_e:float, Dv_e:float, V_sat_p:float, V_sat_m:float, A:float) -> (v_s:float, Dv_s:float) :*\n",
    "\n",
    "(les \":float\" et \"-> (vs:float, Dv_s:float)\" sont là pour vous indiquez les type et sorties attendues, ils ne sont pas à recopier)\n",
    "\n",
    "qui prends en argument les nombres :\n",
    "- *v_e*, tension à l'entrée de l'amplificateur $v_e$\n",
    "- *Dv_e*, la dérivée de la tension d'entrée de l'amplificateur $\\dfrac{\\mathrm{d}v_e}{\\mathrm{d}t}$\n",
    "- *V_sat_p*, tension de saturation positive de l'amplificateur $V_{sat,+}$\n",
    "- *V_sat_m*, tension de saturation négative de l'amplificateur $V_{sat,-}$\n",
    "- *A*, le gain de l'amplificateur $A$\n",
    "\n",
    "et qui renvoie un tuple de nombres :\n",
    "- *v_s*, tension à la sortie de l'amplificateur $v_s$\n",
    "- *Dv_s*, la dérivée de la tension de sortie de l'amplificateur $\\dfrac{\\mathrm{d}v_s}{\\mathrm{d}t}$\n",
    "\n",
    "Nous reprendrons la fonction précédente que nous améliorerons"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Bloc filtre passe-bande du second ordre\n",
    "\n",
    "Un filtre passe-bande du second ordre est caractérisé par sa fonction de transfert :\n",
    "\n",
    "$\n",
    "\\begin{equation}\n",
    "\\underline{H} = \\dfrac{G}{1+jQ\\left(\\dfrac{\\omega}{\\omega_0}-\\dfrac{\\omega_0}{\\omega}\\right)}\n",
    "\\end{equation}\n",
    "$\n",
    "\n",
    "Comme nous ferons la simulation de l'évolution temporelle des oscillateurs nous allons nous interesser plutôt à l'équation différentielle correspondante :\n",
    "\n",
    "$\n",
    "\\begin{equation}\n",
    "\\dfrac{\\mathrm{d}^2v_s}{\\mathrm{d}t^2}+\\dfrac{\\omega_0}{Q}\\dfrac{\\mathrm{d}v_s}{\\mathrm{d}t}+\\omega_0^2v_s = \\dfrac{G\\omega_0}{Q}\\dfrac{\\mathrm{d}v_e}{\\mathrm{d}t}\n",
    "\\end{equation}\n",
    "$\n",
    "\n",
    "avec $G$ le gain à résonance, $Q$ le facteur de qualité et $\\omega_0$ la pulsation propre.\n",
    "\n",
    "**Méthode d'Euler explicite**\n",
    "\n",
    "Cette équation différentielle étant du second ordre nous devons la ré-écrire comme une équation du premier ordre pour appliquer une méthode d'Euler explicite à sa résolution numérique.\n",
    "\n",
    "Pour cela nous allons définir deux variables pour les tensions d'entrée et de sortie qui sont les fonctions et leurs dérivées : ($v_s$ et $\\dfrac{\\mathrm{d}v_s}{\\mathrm{d}t}$) ainsi que ($v_e$ et $\\dfrac{\\mathrm{d}v_e}{\\mathrm{d}t}$) que nous rassemblerons dans les vecteurs :\n",
    "$\n",
    "\\begin{pmatrix}\n",
    "v_s \\\\\n",
    "\\dfrac{\\mathrm{d}v_s}{\\mathrm{d}t}\n",
    "\\end{pmatrix}\n",
    "$\n",
    " et \n",
    "$\n",
    "\\begin{pmatrix}\n",
    "v_e \\\\\n",
    "\\dfrac{\\mathrm{d}v_e}{\\mathrm{d}t}\n",
    "\\end{pmatrix}\n",
    "$\n",
    "\n",
    "\n",
    "On obtient donc une nouvelle écriture pour l'équation différentielle :\n",
    "$\n",
    "\\dfrac{\\mathrm{d}}{\\mathrm{d}t}\\left(\n",
    "\\begin{pmatrix}\n",
    "v_s \\\\\n",
    "\\dfrac{\\mathrm{d}v_s}{\\mathrm{d}t}\n",
    "\\end{pmatrix}\n",
    "\\right)\n",
    "=\n",
    "\\begin{pmatrix}\n",
    "\\dfrac{\\mathrm{d}v_s}{\\mathrm{d}t} \\\\\n",
    "-\\dfrac{\\omega_0}{Q}\\dfrac{\\mathrm{d}v_s}{\\mathrm{d}t}-\\omega_0^2v_s + \\dfrac{G\\omega_0}{Q}\\dfrac{\\mathrm{d}v_e}{\\mathrm{d}t}\n",
    "\\end{pmatrix}\n",
    "$\n",
    "\n",
    "La méthode d'Euler explicite est alors basée sur l'approximation de la dérivée par son taux d'acroissement suivante :\n",
    "$\n",
    "\\begin{equation}\n",
    "\\dfrac{\\mathrm{d}y}{\\mathrm{d}t} = f(y(t),x(t)) \\Rightarrow y(t+\\mathrm{d}t) \\simeq y(t) + f(y(t),x(t))dt\n",
    "\\end{equation}\n",
    "$\n",
    "\n",
    "Ce qui se traduit dans notre cas par l'approximation :\n",
    "\n",
    "$\n",
    "\\begin{equation}\n",
    "\\begin{pmatrix}\n",
    "v_s(t+\\mathrm{d}t) \\\\\n",
    "\\dfrac{\\mathrm{d}v_s}{\\mathrm{d}t}(t+\\mathrm{d}t)\n",
    "\\end{pmatrix}\n",
    "\\simeq\n",
    "\\begin{pmatrix}\n",
    "v_s(t) \\\\\n",
    "\\dfrac{\\mathrm{d}v_s}{\\mathrm{d}t}(t)\n",
    "\\end{pmatrix}\n",
    "+\n",
    "\\begin{pmatrix}\n",
    "\\dfrac{\\mathrm{d}v_s}{\\mathrm{d}t}(t)\\times \\mathrm{d}t \\\\\n",
    "-\\dfrac{\\omega_0}{Q}\\dfrac{\\mathrm{d}v_s}{\\mathrm{d}t}(t)\\times \\mathrm{d}t -\\omega_0^2v_s(t)\\times \\mathrm{d}t  + \\dfrac{G\\omega_0}{Q}\\dfrac{\\mathrm{d}v_e}{\\mathrm{d}t}(t)\\times \\mathrm{d}t \n",
    "\\end{pmatrix}\n",
    "\\end{equation}\n",
    "$\n",
    "\n",
    "Soit deux équations :\n",
    "\n",
    "$\n",
    "\\begin{equation}\n",
    "v_s(t+\\mathrm{d}t) \\simeq v_s(t)+\\dfrac{\\mathrm{d}v_s}{\\mathrm{d}t}(t)\\times \\mathrm{d}t \n",
    "\\end{equation}\n",
    "$\n",
    "\n",
    "et\n",
    "\n",
    "$\n",
    "\\begin{equation}\n",
    "\\dfrac{\\mathrm{d}v_s}{\\mathrm{d}t}(t+\\mathrm{d}t) \\simeq \\dfrac{\\mathrm{d}v_s}{\\mathrm{d}t}(t) -\\dfrac{\\omega_0}{Q}\\dfrac{\\mathrm{d}v_s}{\\mathrm{d}t}(t)\\times \\mathrm{d}t -\\omega_0^2v_s(t)\\times \\mathrm{d}t  + \\dfrac{G\\omega_0}{Q}\\dfrac{\\mathrm{d}v_e}{\\mathrm{d}t}(t)\\times \\mathrm{d}t\n",
    "\\end{equation}\n",
    "$\n",
    "\n",
    "**QUESTION 6**\n",
    "\n",
    "Définir une fonction \n",
    "\n",
    "*def filtre_passe_bande(v_s:float, Dv_s:float, v_e:float, Dv_e:float, omega_0:float, Q:float, G:float, dt:float) -> (v_s_prime:float, Dv_s_prime:float) :*\n",
    "\n",
    "qui prends en argument les nombres :\n",
    "- *v_s* la tension de sortie à l'instant $t$, soit $v_s(t)$\n",
    "- *Dv_s* la dérivée de la tension de sortie à l'instant $t$, soit $\\dfrac{\\mathrm{d}v_s}{\\mathrm{d}t}(t)$\n",
    "- *v_e* la tension d'entrée à l'instant $t$, soit $v_e(t)$\n",
    "- *Dv_e* la dérivée de la tension d'entrée à l'instant $t$, soit $\\dfrac{\\mathrm{d}v_e}{\\mathrm{d}t}(t)$\n",
    "- *omega_0* la pulsation de résonance du filtre\n",
    "- *Q* le facteur de qualité du filtre\n",
    "- *G* le gain à résonance du filtre\n",
    "- *dt* le pas de temps choisi pour la méthode d'Euler explicite\n",
    "\n",
    "et qui renvoie le tuple de nombres :\n",
    "- *v_s_prime* la tension de sortie à l'instant $t+\\mathrm{d}t$, soit $v_s(t+\\mathrm{d}t)$ obtenue par la méthode d'Euler\n",
    "- *Dv_s_prime* la dérivée de la tension de sortie à l'instant $t+\\mathrm{d}t$, soit $\\dfrac{\\mathrm{d}v_s}{\\mathrm{d}t}(t+\\mathrm{d}t)$ obtenue par la méthode d'Euler"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Testons la simulation du filtre passe-bande en oscillation libre.\n",
    "\n",
    "**QUESTION 7**\n",
    "\n",
    "Définissez des variables et assignez des valeurs aux paramètres :\n",
    "- *omega_0* pulsation propre du filtre $\\omega_0$ (par exemple 1000 rad/s)\n",
    "- *Q* facteur de qualité du filtre $Q$ (par exemple 10)\n",
    "- *G* gain à résonance du filtre $G$ (par exemple 1/3) \n",
    "- *durée* durée totale sur laquelle nous allons calculé l'évolution de la sortie du filtre $\\Delta t$ (par exemple 0.1 s)\n",
    "- *N* nombre de points calculés pendant la durée d'évolution de la sortie du filtre (par exmple 10 000)\n",
    "\n",
    "(n'hésitez pas à tester d'autres valeurs)\n",
    "\n",
    "Définissez une variable et calculez le pas de temps *dt* à partir de la *durée* et du nombre de points *N* comme\n",
    "$\n",
    "\\begin{equation}\n",
    "\\mathrm{d}t = \\dfrac{\\Delta t}{N}\n",
    "\\end{equation}\n",
    "$\n",
    "\n",
    "Définissez un np.array *t* qui comporte les $N$ instants de 0 à $\\Delta t$. On pourra utiliser pour cela la fonction :\n",
    "- *np.linspace(start,stop,num=50)* renvoie un np.array de 50 valeurs espacées équitablement dans l'intervalle [start,stop]\n",
    "\n",
    "Définissez les np.array *entrées*, *Dentrées*, *sorties*, *Dsorties* comportant initialement N éléments égals à 0. On pourra utiliser pour cela la fonction :\n",
    "- *np.zeros(shape)* renvoie un np.array de *shape* zéros\n",
    "\n",
    "Fixer les conditions initiales en assignant une valeur non nulle à l'élément d'indice 0 du np.array *sorties*\n",
    "\n",
    "A l'aide d'une boucle *for*  remplissez tous les éléments d'indice *i+1* des np.array *sorties* et *Dsorties* à l'aide de votre fonction *filtre_passe_bande* qui les calculera à partir des éléments d'indice *i* des np.array *sorties*, *Dsorties*, *entrées* et *Dentrées*.\n",
    "\n",
    "Ainsi vous simulerez l'évolution par pas de $dt$ de 0 à $\\Delta t$ de la tension de sortie du filtre $v_s(t)$, avec une tension d'entrée nulle $v_e(t) = 0$. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Affichons le résultat de notre calcul pour observer si la simulation correspond à nos attentes.\n",
    "\n",
    "**QUESTION 8**\n",
    "\n",
    "Tracez le np.array *sorties* en fonction du np.array *t* sous forme de point. On pourra utiliser les fonctions :\n",
    "- *plt.plot(x,y,'.')* trace y en fonction de x avec des points\n",
    "- *plt.xlabel(\"abscisses\")* affiche la chaîne de caractères *abscisses* sous l'axe des abscisses\n",
    "- *plt.ylabel(\"ordonnées\")* affiche la chaîne de caractères *ordonnées* à côté de l'axe des ordonnées\n",
    "- *plt.legend([\"légende\"])* affiche la chaîne de caractères *légende* en légende\n",
    "- *plt.show()* affiche le graphique prè-définit\n",
    "- *plt.close()* implique que les prochains tracés se feront sur un autre graphique"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Mise en cascade des blocs amplificateur et filtre passe-bande du second ordre\n",
    "\n",
    "Nous avons réalisé les blocs nécessaire à la simulation d'un oscillateur quasi-sinusoïdal, nous allons donc utiliser les fonctions *filtre_passe_bande()* et *sortie_amplificateur()* précédentes de manière alternée pour simuler le fonction bouclé de l'oscillateur.\n",
    "\n",
    "Commençons tout d'abord par définir les paramètres des deux blocs.\n",
    "\n",
    "**QUESTION 9**\n",
    "\n",
    "Définissez des variables et assignez des valeurs aux paramètres :\n",
    "- *V_sat_p* tension de saturation positive (par exemple 14.6 V)\n",
    "- *V_sat_m* tension de saturation négative (par exemple -13.4 V)\n",
    "- *A* gain de l'amplificateur (par exemple 3.1) \n",
    "- *omega_0* pulsation propre du filtre $\\omega_0$ (par exemple 1000 rad/s)\n",
    "- *Q* facteur de qualité du filtre $Q$ (par exemple 1/3)\n",
    "- *G* gain à résonance du filtre $G$ (par exemple 1/3)\n",
    "- *durée* durée totale sur laquelle nous allons calculé l'évolution de la sortie du filtre $\\Delta t$ (par exemple 0.3 s)\n",
    "- *N* nombre de points calculés pendant la durée d'évolution de la sortie du filtre (par exemple 10 000)\n",
    "\n",
    "Définissez une variable et calculez le pas de temps *dt* à partir de la *durée* et du nombre de points *N* comme\n",
    "$\n",
    "\\begin{equation}\n",
    "\\mathrm{d}t = \\dfrac{\\Delta t}{N}\n",
    "\\end{equation}\n",
    "$\n",
    "\n",
    "Définissez un np.array *t* qui comporte les $N$ instants de 0 à $\\Delta t$. On pourra utiliser pour cela la fonction :\n",
    "- *np.linspace(start,stop,num=50)* renvoie un np.array de 50 valeurs espacées équitablement dans l'intervalle [start,stop]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Pour la suite nous appelerons $v_1(t)$ l'entrée du filtre et $v_2(t)$ la sortie, définissons les objets dans lesquels nous stockerons ces grandeurs $v_1(t)$, $\\dfrac{\\mathrm{d}v_1}{\\mathrm{d}t}$, $v_2(t)$ et $\\dfrac{\\mathrm{d}v_2}{\\mathrm{d}t}$ ainsi que les conditions initiales correspondantes.\n",
    "\n",
    "**QUESTION 10**\n",
    "\n",
    "Définissez les np.array *v_1*, *Dv_1*, *v_2*, *Dv_2* comportant initialement N éléments égals à 0. On pourra utiliser pour cela la fonction :\n",
    "- *np.zeros(shape)* renvoie un np.array de *shape* zéros\n",
    "\n",
    "Fixer les conditions initiales en assignant une valeur non nulle au premier élément du np.array *Dv_1* (par exemple Dv_1[0] = 1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Pour le fonctionnement en système bouclé de l'oscillateur, il s'agit donc maintenant d'exécuter de manière alternée les fonctions *filtre_passe_bande* et *sortie_amplificateur* pour calculer à chaque pas de temps $\\mathrm{d}t$ les valeurs de $v_1(t)$, $\\dfrac{\\mathrm{d}v_1}{\\mathrm{d}t}$, $v_2(t)$ et $\\dfrac{\\mathrm{d}v_2}{\\mathrm{d}t}$.\n",
    "\n",
    "**QUESTION 11**\n",
    "\n",
    "A l'aide d'une boucle *for*,\n",
    "\n",
    "calculez d'abord les éléments d'indice *i+1* des np.array *v_2* et *Dv_2* à l'aide de la fonction *filtre_passe_bande* et des éléments d'indice *i* des np.array *v_1*, *Dv_1*, *v_2*, *Dv_2*,\n",
    "\n",
    "puis calculez les éléments d'indice *i+1* des np.array *v_1* et *Dv_1* à l'aide de la fonction *sortie_amplificateur* et des éléments d'indice *i+1* des np.array *v_2* et *Dv_2* que vous venez de calculer"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Affichons maintenant les résultats obtenus pour visualiser le démarrage et le régime permanent des oscillations.\n",
    "\n",
    "**QUESTION 12**\n",
    "\n",
    "Tracez les np.array *v_1* et *v_2* en fonction du np.array *t* sous forme de trait. On pourra utiliser les fonctions :\n",
    "- *plt.plot(x,y,'-')* trace y en fonction de x avec des traits\n",
    "- *plt.xlabel(\"abscisses\")* affiche la chaîne de caractères *abscisses* sous l'axe des abscisses\n",
    "- *plt.ylabel(\"ordonnées\")* affiche la chaîne de caractères *ordonnées* à côté de l'axe des ordonnées\n",
    "- *plt.legend([\"légende1\",\"légende2\"])* affiche les chaînes de caractères *légende1* et *légende2* en légende\n",
    "- *plt.show()* affiche le graphique prè-définit\n",
    "- *plt.close()* implique que les prochains tracés se feront sur un autre graphique"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Blocs de l'oscillateur de relaxation\n",
    "\n",
    "## Bloc comparateur à hystérésis non-inverseur\n",
    "\n",
    "Le bloc comparateur à hystérésis non-inverseur est caractérisé par deux éléments :\n",
    "- ses tensions seuil de basculement $V_{0,+}$ et $V_{0,-}$\n",
    "- ses tensions de saturation $V_{sat,+}$ et $V_{sat,-}$\n",
    "\n",
    "**QUESTION 13**\n",
    "\n",
    "Définir une fonction\n",
    "\n",
    "*def comparateur_hystérésis(v_e:float, v_s:float, V_sat_p:float, V_sat_m:float, V_0_p:float, V_0_m:float) -> v_s_prime:float :*\n",
    "\n",
    "qui prends en argument les nombres :\n",
    "- *v_e*, tension à l'entrée du comparateur $v_e$\n",
    "- *v_s*, tension précédente à la sortie du comparateur $v_s$\n",
    "- *V_sat_p*, tension de saturation positive du comparateur $V_{sat,+}$\n",
    "- *V_sat_m*, tension de saturation négative du comparateur $V_{sat,-}$\n",
    "- *V_0_p*, tension seuil de basculement positive du comparateur $V_{0,+}$\n",
    "- *V_0_m*, tension seuil de basculement négative du comparateur $V_{0,-}$\n",
    "\n",
    "et qui renvoie le nombre :\n",
    "- *v_s_prime*, nouvelle tension à la sortie de l'amplificateur $v_s$\n",
    "\n",
    "Nous ferons attention à distinguer les cas de saturation positive ou négative précédente pour écrire notre fonction."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Testons notre fonction *comparateur_hystérésis*\n",
    "\n",
    "**QUESTION 14**\n",
    "\n",
    "Définissez des variables et assignez des valeurs pour les paramètres :\n",
    "- *V_sat_p* tension de saturation positive (par exemple +15V)\n",
    "- *V_sat_m* tension de saturation négative (par exemple -15V)\n",
    "- *V_0_p* tension de saturation positive (par exemple 7.5V)\n",
    "- *V_0_m* tension de saturation négative (par exemple -7.5V)\n",
    "- *N* le nombre de points qui doit être pair (par exemple 1000)\n",
    "\n",
    "(n'hésitez pas à tester d'autres valeurs)\n",
    "\n",
    "Définissez des valeurs de tensions d'entrée à tester avec le np.array *entrées* qui sera une liste de N valeurs allant de *V_sat_m* à *V_sat_p* puis de *V_sat_p* à *V_sat_m*. On pourra utiliser pour cela les fonctions :\n",
    "- *np.linspace(start,stop,num=50)* renvoie un np.array de 50 valeurs espacées équitablement dans l'intervalle [start,stop]\n",
    "- *np.concatenate((a1,a2),axis=0)* renvoie la concaténation des listes *a1* et *a2*\n",
    "\n",
    "Définissez le np.array *sorties* qui sera une liste de N zéros dans laquelle on stockera les résultats de notre fonction *comparateur_hystérésis* pour chaque élément d'*entrées*. On pourra utiliser pour cela la fonction :\n",
    "- *np.zeros(shape)* renvoie un np.array de *shape* zéros\n",
    "\n",
    "Initialisez le premier élément de *sorties* à *V_sat_m*\n",
    "\n",
    "A l'aide d'une boucle *for* assignez à chaque éléments *sorties[i+1]* la valeur renvoyé par votre fonction *comparateur_hystérésis* prenant en arguments les paramètres précédents *V_sat_p*, *V_sat_m*, *V_0_p*, *V_0_m*, la tension d'entrée *entrées[i]* et la tension de sortie *sorties[i]*"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Affichons les résultats de notre test à l'aide d'un graphique afin de mieux contrôler visuellement le bon fonctionnement de notre fonction.\n",
    "\n",
    "**QUESTION 15**\n",
    "\n",
    "Tracez le np.array *sorties* en fonction du np.array *entrées* sous forme de trait. On pourra utiliser les fonctions :\n",
    "- *plt.plot(x,y,'-')* trace y en fonction de x avec des traits\n",
    "- *plt.xlabel(\"abscisses\")* affiche la chaîne de caractères *abscisses* sous l'axe des abscisses\n",
    "- *plt.ylabel(\"ordonnées\")* affiche la chaîne de caractères *ordonnées* à côté de l'axe des ordonnées\n",
    "- *plt.legend([\"légende\"])* affiche la chaîne de caractères *légende* en légende\n",
    "- *plt.show()* affiche le graphique prè-définit\n",
    "- *plt.close()* implique que les prochains tracés se feront sur un autre graphique"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Bloc intégrateur inverseur\n",
    "\n",
    "Un montage intégrateur inverseur réel est caractérisé par sa fonction de transfert :\n",
    "\n",
    "$\n",
    "\\begin{equation}\n",
    "\\underline{H} = \\dfrac{-\\tau^\\prime/\\tau}{1+j\\tau^\\prime\\omega}\n",
    "\\end{equation}\n",
    "$\n",
    "\n",
    "Comme nous allons étudier l'évolution temporelle des tensions, nous nous interresserons plutôt à l'équation différentielle :\n",
    "\n",
    "$\n",
    "\\begin{equation}\n",
    "\\dfrac{\\mathrm{d}v_s}{\\mathrm{d}t}+\\dfrac{v_s}{\\tau^\\prime} = -\\dfrac{v_e}{\\tau}\n",
    "\\end{equation}\n",
    "$\n",
    "\n",
    "avec $\\tau^\\prime \\gg \\tau$ afin d'avoir un comportement intégrateur pour les pulsations $\\omega \\gg \\dfrac{1}{\\tau^\\prime}$.\n",
    "\n",
    "**Méthode d'Euler explicite**\n",
    "\n",
    "L'équation différentielle étant déjà une équation du premier ordre, la méthode d'Euler sera plus facile a explicité comme\n",
    "\n",
    "$\n",
    "\\begin{equation}\n",
    "\\dfrac{\\mathrm{d}v_s}{\\mathrm{d}t} = -\\dfrac{v_s}{\\tau^\\prime} -\\dfrac{v_e}{\\tau}\n",
    "\\end{equation}\n",
    "$\n",
    "\n",
    "et que la méthode d'Euler est basée sur l'approximation $v_s(t+\\mathrm{d}t) \\simeq v_s(t) + \\dfrac{\\mathrm{d}v_s}{\\mathrm{d}t}\\times \\mathrm{d}t$\n",
    "\n",
    "On obtient la relation\n",
    "\n",
    "$\n",
    "\\begin{equation}\n",
    "v_s(t+\\mathrm{d}t) \\simeq v_s(t) -\\left(\\dfrac{v_s(t)}{\\tau^\\prime} + \\dfrac{v_e(t)}{\\tau}\\right)\\times\\mathrm{d}t\n",
    "\\end{equation}\n",
    "$\n",
    "\n",
    "**QUESTION 16**\n",
    "\n",
    "Définir une fonction \n",
    "\n",
    "*def integrateur_inverseur_reel(v_s:float, v_e:float, tau:float, tau_prime:float, dt:float) -> v_s_prime:float :*\n",
    "\n",
    "qui prends en argument les nombres :\n",
    "- *v_s* la tension de sortie à l'instant $t$, soit $v_s(t)$\n",
    "- *v_e* la tension d'entrée à l'instant $t$, soit $v_e(t)$\n",
    "- *tau* la constante de temps d'intégration $\\tau$\n",
    "- *tau_prime* la constante de temps de la bande de fréquence intégrée $\\tau^\\prime$\n",
    "- *dt* le pas de temps choisi pour la méthode d'Euler explicite\n",
    "\n",
    "et qui renvoie le tuple de nombres :\n",
    "- *v_s_prime* la tension de sortie à l'instant $t+\\mathrm{d}t$, soit $v_s(t+\\mathrm{d}t)$ obtenue par la méthode d'Euler"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Testons la simulation de l'intégrateur inverseur réel en régime sinusoïdal.\n",
    "\n",
    "**QUESTION 17**\n",
    "\n",
    "Définissez des variables et assignez des valeurs aux paramètres :\n",
    "- *tau* la constante de temps d'intégration $\\tau$ (par exemple 0.001 s)\n",
    "- *tau_prime* la constante de temps de la bande de fréquence intégrée $\\tau^\\prime$ (par exemple 0.1 s)\n",
    "- *durée* durée totale sur laquelle nous allons calculé l'évolution de la sortie de l'intégrateur $\\Delta t$ (par exemple 0.01 s)\n",
    "- *N* nombre de points calculés pendant la durée d'évolution de la sortie de l'intégrateur (par exemple 10 000)\n",
    "\n",
    "(n'hésitez pas à tester d'autres valeurs)\n",
    "\n",
    "Définissez une variable et calculez le pas de temps *dt* à partir de la *durée* et du nombre de points *N* comme\n",
    "$\n",
    "\\begin{equation}\n",
    "\\mathrm{d}t = \\dfrac{\\Delta t}{N}\n",
    "\\end{equation}\n",
    "$\n",
    "\n",
    "Définissez un np.array *t* qui comporte les $N$ instants de 0 à $\\Delta t$. On pourra utiliser pour cela la fonction :\n",
    "- *np.linspace(start,stop,num=50)* renvoie un np.array de 50 valeurs espacées équitablement dans l'intervalle [start,stop]\n",
    "\n",
    "Définissez le np.array *sorties* comportant initialement N éléments égals à 0. On pourra utiliser pour cela la fonction :\n",
    "- *np.zeros(shape)* renvoie un np.array de *shape* zéros\n",
    "\n",
    "Définissez le np.array *entrée* comme une fonction cosinus de *t*.\n",
    "\n",
    "A l'aide d'une boucle *for*  remplissez tous les éléments d'indice *i+1* des np.array *sorties* à l'aide de votre fonction *integrateur_inverseur_reel* qui les calculera à partir des éléments d'indice *i* des np.array *sorties* et *entrées*.\n",
    "\n",
    "Ainsi vous simulerez l'évolution par pas de $dt$ de 0 à $\\Delta t$ de la tension de sortie de l'intégrateur $v_s(t)$, avec une tension d'entrée sinusoïdale. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Affichons le résultat de notre calcul pour observer si la simulation correspond à nos attentes.\n",
    "\n",
    "**QUESTION 18**\n",
    "\n",
    "Tracez les np.array *entrées* et *sorties* en fonction du np.array *t* sous forme de point. On pourra utiliser les fonctions :\n",
    "- *plt.plot(x,y,'.')* trace y en fonction de x avec des points\n",
    "- *plt.xlabel(\"abscisses\")* affiche la chaîne de caractères *abscisses* sous l'axe des abscisses\n",
    "- *plt.ylabel(\"ordonnées\")* affiche la chaîne de caractères *ordonnées* à côté de l'axe des ordonnées\n",
    "- *plt.legend([\"légende1\",\"légende2\"])* affiche les chaînes de caractères *légende1* et *légende2* en légende\n",
    "- *plt.show()* affiche le graphique prè-définit\n",
    "- *plt.close()* implique que les prochains tracés se feront sur un autre graphique"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Mise en cascade des blocs comparateur à hystérésis et intégrateur inverseur réel\n",
    "\n",
    "Nous avons réalisé les blocs nécessaire à la simulation d'un oscillateur de relaxation, nous allons donc utiliser les fonctions *comparateur_hystérésis()* et *integrateur_inverseur_reel()* précédentes de manière alternée pour simuler le fonction bouclé de l'oscillateur.\n",
    "\n",
    "Commençons tout d'abord par définir les paramètres des deux blocs.\n",
    "\n",
    "**QUESTION 19**\n",
    "\n",
    "Définissez des variables et assignez des valeurs aux paramètres :\n",
    "- *V_sat_p* tension de saturation positive\n",
    "- *V_sat_m* tension de saturation négative\n",
    "- *V_0_p* tension seuil de basculement positive\n",
    "- *V_0_m* tension seuil de basculement négative\n",
    "- *tau* la constante de temps de l'intégrateur\n",
    "- *tau_prime* la constante de temps de la bande de pulsation intégrée\n",
    "- *durée* durée totale sur laquelle nous allons calculé l'évolution de la sortie de l'intégrateur $\\Delta t$\n",
    "- *N* nombre de points calculés pendant la durée d'évolution de la sortie de l'intégrateur\n",
    "\n",
    "(par exemple vous pouvez prendre les valeurs précédentes, mais n'hésitez pas à tester ensuite d'autres valeurs)\n",
    "\n",
    "Définissez une variable et calculez le pas de temps *dt* à partir de la *durée* et du nombre de points *N* comme\n",
    "$\n",
    "\\begin{equation}\n",
    "\\mathrm{d}t = \\dfrac{\\Delta t}{N}\n",
    "\\end{equation}\n",
    "$\n",
    "\n",
    "Définissez un np.array *t* qui comporte les $N$ instants de 0 à $\\Delta t$. On pourra utiliser pour cela la fonction :\n",
    "- *np.linspace(start,stop,num=50)* renvoie un np.array de 50 valeurs espacées équitablement dans l'intervalle [start,stop]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Pour la suite nous appelerons $v_1(t)$ l'entrée de l'intégrateur et $v_2(t)$ la sortie, définissons les objets dans lesquels nous stockerons ces grandeurs $v_1(t)$ et $v_2(t)$ ainsi que les conditions initiales correspondantes.\n",
    "\n",
    "**QUESTION 20**\n",
    "\n",
    "Définissez les np.array *v_1* et *v_2* comportant initialement N éléments égals à 0. On pourra utiliser pour cela la fonction :\n",
    "- *np.zeros(shape)* renvoie un np.array de *shape* zéros\n",
    "\n",
    "Fixer les conditions initiales en assignant une valeur de saturation positive à $v_1(t)$."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Pour le fonctionnement en système bouclé de l'oscillateur, il s'agit donc maintenant d'exécuter de manière alternée les fonctions *integrateur_inverseur_reel* et *comparateur_hystérésis* pour calculer à chaque pas de temps $\\mathrm{d}t$ les valeurs de $v_1(t)$ et $v_2(t)$.\n",
    "\n",
    "**QUESTION 21**\n",
    "\n",
    "A l'aide d'une boucle *for*,\n",
    "\n",
    "calculez d'abord l'élément d'indice *i+1* du np.array *v_2* à l'aide de la fonction *integrateur_inverseur_reel* et des éléments d'indice *i* des np.array *v_1* et *v_2*,\n",
    "\n",
    "puis calculez l'élément d'indice *i+1* du np.array *v_1* à l'aide de la fonction *comparateur_hystérésis* et de l'éléments d'indice *i+1* du np.array *v_2* que vous venez de calculer ainsi que de l'élément d'indice *i* du np.array *v_1*."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Affichons maintenant les résultats obtenus.\n",
    "\n",
    "**QUESTION 22**\n",
    "\n",
    "Tracez les np.array *v_1* et *v_2* en fonction du np.array *t* sous forme de trait. On pourra utiliser les fonctions :\n",
    "- *plt.plot(x,y,'-')* trace y en fonction de x avec des traits\n",
    "- *plt.xlabel(\"abscisses\")* affiche la chaîne de caractères *abscisses* sous l'axe des abscisses\n",
    "- *plt.ylabel(\"ordonnées\")* affiche la chaîne de caractères *ordonnées* à côté de l'axe des ordonnées\n",
    "- *plt.legend([\"légende1\",\"légende2\"])* affiche les chaînes de caractères *légende1* et *légende2* en légende\n",
    "- *plt.show()* affiche le graphique prè-définit\n",
    "- *plt.close()* implique que les prochains tracés se feront sur un autre graphique"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python [conda env:base] *",
   "language": "python",
   "name": "conda-base-py"
  },
  "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.7"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
