GDET3 Diskrete Faltung GUI.ipynb 12.5 KB
Newer Older
1
2
3
4
5
{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
6
7
8
9
10
   "metadata": {
    "jupyter": {
     "source_hidden": true
    }
   },
11
12
13
14
15
16
   "outputs": [],
   "source": [
    "# Copyright 2019 Institut für Nachrichtentechnik, RWTH Aachen University\n",
    "%matplotlib widget\n",
    "\n",
    "import ipywidgets as widgets\n",
17
    "from ipywidgets import interact, interactive, fixed, Layout, HBox, VBox\n",
18
19
20
21
22
23
24
25
26
27
28
29
    "from IPython.display import clear_output, display, HTML\n",
    "\n",
    "from scipy import signal # convolution\n",
    "\n",
    "from ient_nb.ient_plots import *\n",
    "from ient_nb.ient_signals import *"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
30
31
32
33
34
35
36
    "<div>\n",
    "    <img src=\"ient_nb/figures/rwth_ient_logo@2x.png\" style=\"float: right;height: 5em;\">\n",
    "</div>\n",
    "\n",
    "# Demonstrator Diskrete Faltung\n",
    "\n",
    "Zum Starten: Im Menü: Run <span class=\"fa-chevron-right fa\"></span> Run All Cells auswählen."
37
38
39
40
41
42
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
43
    "## Einleitung\n",
44
45
46
47
48
49
    "Ein diskretes Signal ist ein Signal, welches nur für ganzzahlige Werte $n$ einen Wert annimmt und sonst Null ist. Auch für die Elementarsignale existieren jeweils zeitdiskrete Versionen. Diese Demonstration nutzt einige davon. \n",
    "\n",
    "Diskrete Signale können genau wie kontinuierliche Signale gefaltet werden. \n",
    "Im Folgenden wird die diskrete Faltung\n",
    "$$g(n)=s(n)\\ast h(n)=\\sum_{m=-\\infty}^{\\infty}s(m)h(n-m)$$\n",
    "betrachtet und veranschaulicht.\n",
50
51
    "\n",
    "## Demo\n",
52
53
54
55
56
57
58
    "In der Demonstration stehen verschiedene Signale für $s(n)$ und $h(n)$ zur Verfügung. \n",
    "$s(n)$ und $h(n)$ können gewählt werden als zeitdiskrete Varianten von:\n",
    "* Dirac-Impuls  $\\delta(n)$\n",
    "* Sprungfunktion  $\\epsilon(n)$\n",
    "* Exponentialimpuls  $\\epsilon(n)\\cdot\\mathrm{b}^{n}$\n",
    "* Rechteckfunktion  $rect(n) = \\epsilon(n+M)-\\epsilon(n-M-1)$\n",
    "\n",
59
60
61
62
63
64
65
66
67
68
69
    "Unter der folgenden Abbildungen können diese Funktionen ausgewählt werden und, falls gewünscht, eine Verschiebung um $n_0$ eingestellt werden. Für den Exponentialimpuls ist ebenfalls der Faktor $b$ variierbar, für die Rechteckfunktion ist die Breite $M$ wählbar. In der Abbildung können dann die gewählten Funktionen mit ihren Parametern betrachtet werden.\n",
    "\n",
    "Zusätzlich zu Elementarsignalen kann auch eine frei definierbare Funktion $s_0(t)$ zur Faltung verwendet werden."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
70
    "s_0 = lambda n: gauss(n)"
71
72
73
74
75
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
76
77
78
79
80
   "metadata": {
    "jupyter": {
     "source_hidden": true
    }
   },
81
82
83
84
85
86
   "outputs": [],
   "source": [
    "n = np.linspace(-10, 10, 21)\n",
    "m = np.linspace(-40, 40, 81) # m Achse\n",
    "n0 = -2\n",
    "\n",
87
88
    "container_s = container_h = None\n",
    "\n",
89
90
91
92
    "def convolution(s, h):\n",
    "    # Convolve s and h numerically\n",
    "    return signal.convolve(s(m), h(m), mode='same')\n",
    "\n",
93
94
95
    "signal_types = {'Dirac-Impuls'       : lambda n, b, M: np.where(n==0, 1, 0),\n",
    "                'Sprungfunktion'     : lambda n, b, M: np.where(n>=0, 1, 0),\n",
    "                'Exponentialimpuls'  : lambda n, b, M: unitstep(n)*b**n,\n",
96
97
    "                'Rechteck'           : lambda n, b, M: unitstep(n+M) - unitstep(n-M-1),\n",
    "                'Eigene Kreation s0(n)' : lambda n, b, M: s_0(n)\n",
98
99
    "               }\n",
    "\n",
100
101
102
103
104
    "fig0, axs0 = plt.subplots(1, 2, figsize=(ient_fig_width, ient_fig_width/4));\n",
    "def update_signals(s_type, s_n0, h_type, h_n0, b_s, b_h, M_s, M_h):\n",
    "    # show widgets according to chosen s and h\n",
    "    w_b_s.layout.visibility = 'visible' if s_type == 'Exponentialimpuls' else 'hidden'; w_M_s.layout.visibility = 'visible' if s_type == 'Rechteck' else 'hidden'\n",
    "    w_b_h.layout.visibility = 'visible' if h_type == 'Exponentialimpuls' else 'hidden'; w_M_h.layout.visibility = 'visible' if h_type == 'Rechteck' else 'hidden'\n",
105
    "    \n",
106
    "    # calculate s(m-n0) and h(m-n0)\n",
107
    "    global s, h, gn, container_s, container_h # reused in second interactive plot\n",
108
109
    "    s = lambda m: signal_types[s_type]((m-s_n0), b_s, M_s); # s(m-n0)\n",
    "    h = lambda m: signal_types[h_type]((m-h_n0), b_h, M_h); # h(m-n0)\n",
110
    "    gn = convolution(s, h) # numerical convolution\n",
111
112
    "    \n",
    "    # update second plot if existing\n",
113
114
115
116
117
118
    "    try:\n",
    "        global n0\n",
    "        update_plot(n0)\n",
    "    except NameError:\n",
    "        pass\n",
    "    \n",
119
    "    # display s and h plots\n",
120
    "    if container_s is None:\n",
121
    "        # plot s\n",
122
123
124
125
    "        ax = axs0[0]; \n",
    "        container_s = ient_stem(ax, m, s(m), 'rwth')\n",
    "        ax.set_xticks(np.arange(-10, 11, step=2))\n",
    "        ax.set_xlabel(r'$\\rightarrow n$'); ax.set_ylabel(r'$\\uparrow s(n)$')\n",
126
    "        ax.set_xlim([-10.9, 10.9]); ient_axis(ax); ient_grid(ax);\n",
127
    "    \n",
128
    "        # plot h\n",
129
130
131
132
    "        ax = axs0[1]; \n",
    "        container_h = ient_stem(ax, m, h(m), 'rwth')\n",
    "        ax.set_xticks(np.arange(-10, 11, step=2))\n",
    "        ax.set_xlabel(r'$\\rightarrow n$'); ax.set_ylabel(r'$\\uparrow h(n)$')\n",
133
    "        ax.set_xlim(axs0[0].get_xlim()); ient_axis(ax); ient_grid(ax);\n",
134
    "\n",
135
136
    "    else:\n",
    "        ient_stem_set_ydata(container_s, s(m))\n",
137
138
    "        ient_stem_set_ydata(container_h, h(m))\n",
    "        \n",
139
140
141
    "    # update limits\n",
    "    ient_update_ylim(axs0[0], s(m), .19)\n",
    "    ient_update_ylim(axs0[1], h(m), .19)\n",
142
    "\n",
143
144
145
146
147
148
149
150
151
152
153
    "# Widgets\n",
    "w_s_type=widgets.Dropdown(options=list(signal_types.keys()), description=r'Wähle $s(n)$:', style=ient_wdgtl_style)\n",
    "w_s_n0=widgets.FloatSlider(min=-5, max=5, value=0, step=1, description=r'Verschiebung $n_0$', style=ient_wdgtl_style)\n",
    "w_h_type=widgets.Dropdown(options=list(signal_types.keys()), description=r'Wähle $h(n)$:', style=ient_wdgtl_style)\n",
    "w_h_n0=widgets.FloatSlider(min=-5, max=5, value=0, step=1, description=r'Verschiebung $n_0$', style=ient_wdgtl_style)\n",
    "w_b_s=widgets.FloatSlider(min=.1, max=1, value=.5, step=.1, description=r'$b_s$', style=ient_wdgtl_style)\n",
    "w_b_h=widgets.FloatSlider(min=.1, max=1, value=.5, step=.1, description=r'$b_h$', style=ient_wdgtl_style)\n",
    "w_M_s=widgets.FloatSlider(min=0, max=5, value=1, step=1, description=r'$M_s$', style=ient_wdgtl_style)\n",
    "w_M_h=widgets.FloatSlider(min=0, max=5, value=1, step=1, description=r'$M_h$', style=ient_wdgtl_style)\n",
    "w = widgets.interactive(update_signals, s_type=w_s_type, s_n0=w_s_n0, h_type=w_h_type, h_n0 =w_h_n0, b_s=w_b_s, b_h=w_b_h, M_s=w_M_s, M_h=w_M_h)\n",
    "display(widgets.HBox((widgets.VBox((w_s_type, w_s_n0, w_b_s, w_M_s)), widgets.VBox((w_h_type, w_h_n0, w_b_h, w_M_h), layout=Layout(margin='0 0 0 100px'))))); w.update();\n"
154
155
   ]
  },
156
157
158
159
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
160
161
162
    "Anschließend kann hier die Faltung $g(n)=s(n)\\ast h(n)$ der zuvor eingestellten Funktionen betrachtet werden.\n",
    "\n",
    "Über den Schieberegler kann der Wert für $n$ verändert werden und die Funktion $h(n-m)$ bewegt sich in der oberen Grafik. In der unteren Grafik ist das resultierende Ausgangssignal $g(n)$ zu sehen."
163
164
   ]
  },
165
166
167
  {
   "cell_type": "code",
   "execution_count": null,
168
169
170
171
172
   "metadata": {
    "jupyter": {
     "source_hidden": true
    }
   },
173
174
   "outputs": [],
   "source": [
175
    "fig, axs = plt.subplots(2, 1, **ient_landscape)\n",
176
177
178
179
180
181
182
183
184
    "global n0\n",
    "container_ss = container_hh = container_gg = None\n",
    "@widgets.interact(n=widgets.FloatSlider(min=-10, max=10, value=n0, step=1, description='Verschiebung $n$', style=ient_wdgtl_style))\n",
    "def update_plot(n):\n",
    "    global container_ss, container_hh, container_gg\n",
    "    global n0\n",
    "    n0 = n\n",
    "    n_ind = np.where(m>=n); n_ind = n_ind[0][0]; g_plot = gn.copy(); g_plot[n_ind+1:] = 0; # hide g(n') with n'>n\n",
    "    if container_gg is None:\n",
185
    "        # plot g = s * h\n",
186
187
188
189
    "        ax = axs[1]\n",
    "        container_gg = ient_stem(ax, m, g_plot)\n",
    "    \n",
    "    if container_ss is not None:\n",
190
    "        # update data\n",
191
192
193
    "        ient_stem_set_ydata(container_ss, s(m))\n",
    "        ient_stem_set_ydata(container_hh, h(n-m))\n",
    "        ient_stem_set_ydata(container_gg, g_plot)\n",
194
195
    "        \n",
    "        # update labels\n",
196
    "        ax = axs[0]\n",
197
    "        ax.texts[0].set_x(n); ax.lines[3].set_xdata([n,n])\n",
198
    "        ax = axs[1]\n",
199
200
    "        ax.texts[0].set_x(n); ax.lines[2].set_xdata([n,n]); ax.texts[0].set_y(np.min(ax.get_ylim()));\n",
    "        ax.lines[3].set_xdata([n, n]); ax.lines[3].set_ydata([0, gn[n_ind]]);\n",
201
    "    else:\n",
202
203
    "        # plot s and h\n",
    "        ax = axs[0];  # s and h axis\n",
204
205
206
207
208
209
    "        container_ss = ient_stem(ax, m, s(m),  'grun', label=r'$s(m)$')\n",
    "        container_ss[0].set_markerfacecolor('none'); \n",
    "        container_ss[0].set_markersize(8); \n",
    "        container_ss[0].set_markeredgewidth(2);\n",
    "        container_hh = ient_stem(ax, m, h(n-m), 'rwth', label=r'$h(n-m)$')\n",
    "        \n",
210
    "        # configure axis settings\n",
211
    "        ax.set_xlabel(r'$\\rightarrow m$');\n",
212
    "        ax.set_xlim([-10.2,10.2]); \n",
213
214
    "        ax.set_xticks(np.arange(-10, 11, step=2))\n",
    "        ax.legend(); ient_grid(ax); ient_axis(ax);\n",
215
    "        ient_annotate_xtick(ax, r'$n$', n, -0.1, 'rwth', 15);  # mark n on m axis\n",
216
    "        \n",
217
    "        # configure g axis settings\n",
218
    "        ax = axs[1]\n",
219
220
    "        ax.set_xlabel(r'$\\rightarrow n$'); ax.set_ylabel(r'$\\uparrow g(n)=s(n)\\ast h(n)$');\n",
    "        ax.set_xlim(axs[0].get_xlim())\n",
221
    "        ax.set_xticks(np.arange(-10, 11, step=2))\n",
222
    "        ient_grid(ax); ient_axis(ax);\n",
223
224
    "        ient_annotate_xtick(ax, r'$n$', n, np.min(ax.get_ylim()), 'black', 15);\n",
    "        ax.plot([n, n], [0, gn[n_ind]], 'ko--', lw=1);\n",
225
    "        \n",
226
227
228
229
230
    "    # update limits\n",
    "    if np.all(gn>0):\n",
    "        gn[0] = 0\n",
    "    ient_update_ylim(axs[0], np.concatenate([s(m), h(n-m)]), .19, ymax = 1e3)\n",
    "    ient_update_ylim(axs[1], gn, np.max(np.abs(gn))/10, ymax = 1e3)"
231
   ]
232
233
  },
  {
234
   "cell_type": "markdown",
235
236
237
238
239
240
241
242
243
244
245
246
247
248
   "metadata": {},
   "source": [
    "## Aufgaben\n",
    "Wähle zunächst für $s(n)$ den Dirac-Impuls und für $h(n)$ verschiedene Funktionen aus. \n",
    "* Beobachte das Faltungsergebnis. Ab welchem $n$ ist ein Ergebnis zu sehen? Was passiert, wenn du eine der Funktionen verschiebst?\n",
    "* Wie sieht das Ergebnis für zwei Rechteckfunktionen aus? Wie für zwei Sprungfunktionen?\n",
    "Wähle nun zwei Rechteckfunktionen mit $M>2$. \n",
    "* Bewege den Schieberegler langsam von links nach rechts. An welcher Stelle tritt der erste Wert des Faltungsergebnisses auf? Wie hoch ist dieser Wert?\n",
    "* Bewege den Schieberegler eins weiter nach rechts. Nun überlagern sich die beiden Signalen an zwei Positionen. Wie hoch ist der Wert des Faltungsergebnisses nun?\n",
    "* Bewege den Schieberegler weiter, bis die beiden Funktionen sich vollständig überlagern. Was passiert, wenn nun der Schieberegler weiter nach rechts geschoben wird? Wie hoch ist der Wert des Faltungsergebnisses? Wie berechnen sich die Höhen des Faltungsergebnisses?"
   ]
  },
  {
   "cell_type": "markdown",
249
   "metadata": {},
250
251
252
253
254
255
   "source": [
    "This notebook is provided as [Open Educational Resource](https://en.wikipedia.org/wiki/Open_educational_resources) (OER). Feel free to use the notebook for your own purposes. The code is licensed under the [MIT license](https://opensource.org/licenses/MIT). \n",
    "\n",
    "Please attribute the work as follows: \n",
    "*Christian Rohlfing, Emin Kosar, Übungsbeispiele zur Vorlesung \"Grundgebiete der Elektrotechnik 3 - Signale und Systeme\"*, gehalten von Jens-Rainer Ohm, 2019, Institut für Nachrichtentechnik, RWTH Aachen University."
   ]
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "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",
274
   "version": "3.8.1"
275
276
277
278
279
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}