Aufgrund einer Wartung wird GitLab am 21.09. zwischen 8:00 und 9:00 Uhr kurzzeitig nicht zur Verfügung stehen. / Due to maintenance, GitLab will be temporarily unavailable on 21.09. between 8:00 and 9:00 am.

Commit d600ab56 authored by Hafiz Emin Kosar's avatar Hafiz Emin Kosar
Browse files

Merge remote-tracking branch 'origin/development-lab' into development-multilang

# Conflicts:
#	GDET3 Faltung GUI.ipynb
#	GDET3 Faltungsbeispiel.ipynb
#	index.ipynb
#	src/laplace/laplace_plot.py
#	src/z_transform/z_transform.py
parents e50e9aef 1bdfc789
Pipeline #370815 passed with stage
in 5 minutes and 9 seconds
......@@ -5,6 +5,3 @@ RUN conda install --quiet --yes \
'scipy==1.4.1' && \
conda clean --all
# Copy workspace
COPY ./ /home/jovyan
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"jupyter": {
"source_hidden": true
}
},
"outputs": [],
"source": [
"# Copyright 2020 Institut für Nachrichtentechnik, RWTH Aachen University\n",
"%matplotlib widget\n",
"\n",
"from ipywidgets import interact, interactive, fixed, HBox, VBox\n",
"import ipywidgets as widgets\n",
"from IPython.display import clear_output, display, HTML\n",
"\n",
"import matplotlib.pyplot as plt\n",
"from rwth_nb.plots import colors\n",
"import rwth_nb.plots.mpl_decorations as rwth_plt\n",
"from rwth_nb.misc.signals import *\n",
"from rwth_nb.misc.transforms import *\n",
"\n",
"t,deltat = np.linspace(-10,10,50001, retstep=True) # t-axis\n",
"f,deltaf = np.linspace(-10,10,len(t), retstep=True) # f-axis\n",
"kMax = 16 # number of k values in sum for Sa(f)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<div>\n",
" <img src=\"figures/rwth_ient_logo@2x.png\" style=\"float: right;height: 5em;\">\n",
"</div>\n",
"\n",
"# Demonstrator Diskrete Fourier-Transformation\n",
"\n",
"Zum Starten: Im Menü: Run <span class=\"fa-chevron-right fa\"></span> Run All Cells auswählen.\n",
"\n",
"## Einleitung\n",
"\n",
"$s(t)$ wird ideal mit Abtastrate $r$ abgetastet.\n",
"\n",
"Diskrete Fourier-Transformation mit $F = \\frac{1}{M}$\n",
"$$\n",
"S_\\mathrm{d}(k) \n",
"= \n",
"\\sum_{n=0}^{M-1} s_\\mathrm{d}(n) \\mathrm{e}^{-\\mathrm{j}2\\pi kFn} \\quad k=0,\\ldots,M-1 \\text{ ,}\n",
"$$\n",
"wobei $s_\\mathrm{d}(n)$ um $M$ periodisches, diskretes Zeitsignal mit $t = n \\cdot \\frac{1}{r}$.\n",
"\n",
"Für die Rücktransformation in den Zeitbereich gilt dann\n",
"$$\n",
"s_\\mathrm{d}(n)\n",
"=\n",
"\\frac{1}{M} \\sum_{k=0}^{M-1}S_\\mathrm{d}(k) \\mathrm{e}^{\\mathrm{j}2\\pi kFn} \\quad n=0,\\ldots,M-1 \\text{ ,}\n",
"$$\n",
"wobei\n",
"$f = k \\cdot \\frac{r}{M}$."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"M = 24; # period of discrete signal\n",
"r = 4; # sampling rate\n",
"\n",
"n = np.arange(M) # discrete time axis\n",
"sd = tri(n/r) + tri((n-M)/r) # discrete signal sd(n)\n",
"\n",
"F = 1/M \n",
"Sd = np.zeros_like(sd, dtype=np.complex)\n",
"for k in np.arange(M): # very slow computation for teaching purpose! Fasten your seatbelts with FFT :)\n",
" Sd[k] = np.sum(sd * np.exp(-2j*np.pi*k*F*n))\n",
"#Sd = np.fft.fft(sd)\n",
"Sd = np.real(Sd) # discard super small imaginary part (1e-16)\n",
"\n",
"# Plot\n",
"fig,axs = plt.subplots(2,1)\n",
"ax = axs[0]; rwth_plt.stem(ax, n, sd); \n",
"ax.set_xlabel(r'$\\rightarrow n$'); ax.set_ylabel(r'$\\uparrow s_\\mathrm{d}(n)$')\n",
"rwth_plt.axis(ax);\n",
"\n",
"ax = axs[1]; rwth_plt.stem(ax, np.arange(M), Sd/r); \n",
"ax.set_xlabel(r'$\\rightarrow k$'); ax.set_ylabel(r'$\\uparrow S_\\mathrm{d}(k)/r$')\n",
"rwth_plt.axis(ax); fig.tight_layout()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Demonstrator"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"jupyter": {
"source_hidden": true
}
},
"outputs": [],
"source": [
"def decorate_second_axis(ax):\n",
" ax.spines['top'].set_position(('axes',.9)); ax.spines['right'].set_color('none'); ax.spines['left'].set_color('none'); ax.spines['bottom'].set_color('none');\n",
" ax.xaxis.set_label_coords(.98,1); ax.xaxis.label.set_horizontalalignment('right') \n",
"\n",
"fig, axs = plt.subplots(2, 1, **rwth_plt.landscape); #fig.tight_layout()\n",
"highlight_rect_style = {'facecolor': \"none\", 'edgecolor': 'rwth:orange', 'linestyle':'--', 'linewidth':2.0}\n",
"\n",
"@widgets.interact( r = widgets.IntSlider(min=0, max=10, step=2, value=0, description='$r$ [Hz]'),\n",
" Mbr = widgets.IntSlider(min=0, max=10, step=2, value=0, description='$M/r$'))\n",
"def update_plots(r, Mbr):\n",
" M = r*Mbr\n",
" global n, snT, k, SkF\n",
" # Continuous functions s(t) and S(f)\n",
" s = tri\n",
" S = lambda f: si(np.pi*f) ** 2\n",
" \n",
" # Ideal sampling\n",
" Sa_f = np.zeros_like(S(f))\n",
" nT = []; snT = nT; n = nT;\n",
" if r > 0:\n",
" T = 1/r\n",
" \n",
" # Sampled function s(n)\n",
" nT, snT = sample(t, s(t), T); n = nT/T;\n",
" \n",
" # Sampled continuous spectrum S_a(f)\n",
" for k in np.arange(-kMax, kMax+1): # evaluate infinite sum only for 2*kMax+1 elements \n",
" Sa_f += S(f-k/T)\n",
" #Sa_f = Sa_f/T # normalize here\n",
" \n",
" # Periodic copies of s(n)\n",
" kF = []; SkF = kF; k = kF;\n",
" sd = np.zeros_like(snT)\n",
" if M > 0:\n",
" F = 1/M # in Hz\n",
" kF, SkF = sample(f, Sa_f, r*F); k = kF/F/r\n",
" for d in np.arange(-kMax, kMax+1): # evaluate infinite sum only for 2*kMax+1 elements \n",
" sd += s((n-d*M)*T)\n",
" display('M={}'.format(M))\n",
" \n",
" # Plot\n",
" if not axs[0].lines: # Call plot() and decorate axes. Usually, these functions take some processing time\n",
" ax = axs[0]; ax.set_title('Zeitbereich');\n",
" ax.plot(t, s(t), 'k', label='$s(t)$');\n",
" ax.set_xlabel(r'$\\rightarrow t $ [s]')\n",
" ax.set_xlim([-5,10]); ax.set_ylim([-.09, 1.19]); ax.legend(loc='upper right', bbox_to_anchor=(1.0, 0.8)); rwth_plt.axis(ax);\n",
" \n",
" axn = ax.twiny(); rwth_plt.stem(axn, [1], [1], color='rwth:blue', label=r'$s(n)$'); \n",
" axn.set_xlabel(r'$\\rightarrow n$'); decorate_second_axis(axn);\n",
" axn.legend(loc='upper right', bbox_to_anchor=(1.0, 0.6));\n",
" axn.fill_between([-.1, M-1+.1], -.05, 1.05, **highlight_rect_style) \n",
" \n",
" ax = axs[1]; ax.set_title('Frequenzbereich');\n",
" ax.plot(f, S(f), 'k', label='$S(f)$')\n",
" ax.plot(f, Sa_f, 'rwth:red', label='$S_\\mathrm{a}(f)/r$')\n",
" ax.set_xlabel(r'$\\rightarrow f $ [Hz]')\n",
" ax.set_xlim([-5,10]); ax.set_ylim([-.09, 1.19]); ax.legend(loc='upper right', bbox_to_anchor=(1.0, 0.8)); rwth_plt.axis(ax);\n",
" \n",
" axk = ax.twiny(); rwth_plt.stem(axk, [1], [1], label=r'$S_\\mathrm{d}(k)/r$');\n",
" axk.set_xlabel(r'$\\rightarrow k$'); decorate_second_axis(axk);\n",
" axk.legend(loc='upper right', bbox_to_anchor=(1.0, 0.5));\n",
" axk.fill_between([-.1, M-1+.1], -.05, 1.05, **highlight_rect_style) \n",
" \n",
" else: # Update only the plot lines themselves. Should not take too much processing time\n",
" ax = axs[0] # time domain\n",
" axn = fig.axes[2];\n",
" if r > 0:\n",
" if len(axn.collections) > 1: axn.collections[1].remove()\n",
" if M == 0:\n",
" rwth_plt.stem_set_data(axn.containers[0], n, snT); #axn.containers[0].set_label(r'$s(n)$')\n",
" else:\n",
" rwth_plt.stem_set_data(axn.containers[0], n, sd); #axn.containers[0].set_label(r'$s_\\mathrm{d}(n)$')\n",
" axn.fill_between([-.1, M-1+.1], -.05, 1.05, **highlight_rect_style) \n",
" \n",
" ax = axs[1] # frequency domain\n",
" ax.lines[1].set_ydata(Sa_f)\n",
" axk = fig.axes[3];\n",
" if M > 0 and r > 0:\n",
" if len(axk.collections) > 1: axk.collections[1].remove()\n",
" axk.fill_between([-.1, M-1+.1], -.05, 1.05, **highlight_rect_style) \n",
" rwth_plt.stem_set_data(axk.containers[0], k, SkF)\n",
" \n",
" axn = fig.axes[2]; axn.set_visible(r > 0)\n",
" axk = fig.axes[3]; axk.set_visible(M > 0 and r > 0)\n",
" if r > 0:\n",
" axn.set_xlim((ax.get_xlim()[0]*r,ax.get_xlim()[1]*r))\n",
" if M > 0:\n",
" axk.set_xlim((ax.get_xlim()[0]*M/r,ax.get_xlim()[1]*M/r))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"---\n",
"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, 2020, Institut für Nachrichtentechnik, RWTH Aachen University."
]
}
],
"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",
"version": "3.8.3"
}
},
"nbformat": 4,
"nbformat_minor": 4
}
......@@ -113,26 +113,84 @@ class pzPlot():
self.ax.set_title(self.translate('Pole-zero plot'), fontsize='12')
self.ax.set_aspect('equal', adjustable='datalim')
def onclick(event):
if self.action == 'add' and event.key == 'shift':
self.action = 'del'
# * move indicates whether a drag is happening
# * selected_coords indicates selected coords when button is pressed
# * ghost_hpoint, ghost_hpoint_conj are the half transparent poles/zeroes to indicate where the newly created will be
self.move = False
self.selected_coords = None
self.ghost_hpoint = None
self.ghost_hpoint_conj = None
def on_btn_press(event):
if event.inaxes != self.ax: return
if self.filter != 'man': return
p = event.xdata + 1j * event.ydata
self.update_point(p, self.mode)
def onkeypress(event):
self.action_locked = True if self.action == 'del' else False
self.w_action_type.children[0].value = self.translate('delete')
# button press, reset move
self.move = False
# find closest point and set selected if there are any valid points nearby
p = event.xdata + 1j * event.ydata
self.selected_coords = p
def on_btn_release(event):
# press + no movement + release = click
if not self.move:
on_click(event)
# press + movement + release = drag
# if dragging process is finished
if self.move and self.find_closest_point(self.selected_coords, self.mode)[0] is not None:
# release move
self.move = False
# remove any ghosts from the figure and clear variables
if self.ghost_hpoint is not None:
self.ghost_hpoint.remove()
self.ghost_hpoint_conj.remove()
self.ghost_hpoint = None
self.ghost_hpoint_conj = None
# delete at press selected point
tmp_action = self.action
self.action = 'del'
self.update_point(self.selected_coords, self.mode)
# add new point at drag point
self.action = 'add'
on_click(event)
self.action = tmp_action
def on_motion(event):
# if button is pressed
if event.button == 1:
# lock move
self.move = True
# if a point was selected
if self.find_closest_point(self.selected_coords, self.mode)[0] is not None:
if self.ghost_hpoint is None:
# draw ghost point
if self.mode == 'p':
plotstyle = rwth_plots.style_poles
else:
plotstyle = rwth_plots.style_zeros
self.ghost_hpoint, = self.ax.plot(event.xdata, event.ydata, **plotstyle, alpha=.5)
if np.abs(event.ydata) > 0: # plot complex conjugated poles/zeroes
self.ghost_hpoint_conj, = self.ax.plot(event.xdata, -event.ydata, **plotstyle, alpha=.5)
else:
self.ghost_hpoint.set_data(event.xdata, event.ydata)
self.ghost_hpoint_conj.set_data(event.xdata, -event.ydata)
def onkeyrelease(event):
if not self.action_locked:
self.w_action_type.children[0].value = self.translate('add')
def on_click(event):
# update point
p = event.xdata + 1j * event.ydata
self.update_point(p, self.mode)
self.fig.canvas.mpl_connect('button_press_event', onclick)
self.fig.canvas.mpl_connect('key_press_event', onkeypress)
self.fig.canvas.mpl_connect('key_release_event', onkeyrelease)
self.fig.canvas.mpl_connect('button_press_event', on_btn_press)
self.fig.canvas.mpl_connect('button_release_event', on_btn_release)
self.fig.canvas.mpl_connect('motion_notify_event', on_motion)
self.handles['axh'] = plt.subplot(gs[0, 1])
self.handles['axh'].set_title(self.translate('impulse response'), fontsize='12')
......@@ -440,7 +498,7 @@ class pzPlot():
if not (roc[0] <= 0 <= roc[1]):
S_f = np.full(self.f.shape, np.NaN)
else:
S_f = rwth_transforms.ilaplace_Hf(self.f, self.H0, poles, zeroes, poles_order, zeroes_order, dB=True)
S_f = rwth_transforms.ilaplace_Hf(self.f, self.H0, poles, zeroes, poles_order, zeroes_order, dB=True).round(8)
# process signals
# delete existing dirac
......
......@@ -112,26 +112,87 @@ class zPlot():
self.ax.set_title(self.translate('Pole-zero plot'), fontsize='12')
self.ax.set_aspect('equal')
def onclick(event):
if self.action == 'add' and event.key == 'shift':
self.action = 'del'
# * move indicates whether a drag is happening
# * selected_coords indicates selected coords when button is pressed
# * ghost_hpoint, ghost_hpoint_conj are the half transparent poles/zeroes to indicate where the newly created will be
self.move = False
self.selected_coords = None
self.ghost_hpoint = None
self.ghost_hpoint_conj = None
def on_btn_press(event):
if event.inaxes != self.ax: return
if self.filter != 'man': return
# button press, reset move
self.move = False
# find closest point and set selected if there are any valid points nearby
p = event.xdata + 1j * event.ydata
self.update_point(p, self.mode)
self.selected_coords = p
def on_btn_release(event):
# press + no movement + release = click
if not self.move:
on_click(event)
# press + movement + release = drag
# if dragging process is finished
if self.move and self.find_closest_point(self.selected_coords, self.mode)[0] is not None:
# release move
self.move = False
# remove any ghosts from the figure and clear variables
if self.ghost_hpoint is not None:
self.ghost_hpoint.remove()
self.ghost_hpoint_conj.remove()
self.ghost_hpoint = None
self.ghost_hpoint_conj = None
# delete at press selected point
tmp_action = self.action
self.action = 'del'
self.update_point(self.selected_coords, self.mode)
# add new point at drag point
self.action = 'add'
on_click(event)
self.action = tmp_action
def on_motion(event):
# if button is pressed
if event.button == 1:
# lock move
self.move = True
# if a point was selected
if self.find_closest_point(self.selected_coords, self.mode)[0] is not None:
if self.ghost_hpoint is None:
# draw ghost point
if self.mode == 'p':
plotstyle = rwth_plots.style_poles
else:
plotstyle = rwth_plots.style_zeros
self.ghost_hpoint, = self.ax.plot(event.xdata, event.ydata, **plotstyle, alpha=.5)
if np.abs(event.ydata) > 0: # plot complex conjugated poles/zeroes
self.ghost_hpoint_conj, = self.ax.plot(event.xdata, -event.ydata, **plotstyle, alpha=.5)
else:
self.ghost_hpoint.set_data(event.xdata, event.ydata)
self.ghost_hpoint_conj.set_data(event.xdata, -event.ydata)
def onkeypress(event):
self.action_locked = self.action == 'del'
self.w_action_type.children[0].value = self.translate('delete')
def on_click(event):
if self.filter != 'man': return
if event.inaxes != self.ax: return
def onkeyrelease(event):
if not self.action_locked:
self.w_action_type.children[0].value = self.translate('add')
# update point
p = event.xdata + 1j * event.ydata
self.update_point(p, self.mode)
self.fig.canvas.mpl_connect('button_press_event', onclick)
self.fig.canvas.mpl_connect('key_press_event', onkeypress)
self.fig.canvas.mpl_connect('key_release_event', onkeyrelease)
self.fig.canvas.mpl_connect('button_press_event', on_btn_press)
self.fig.canvas.mpl_connect('button_release_event', on_btn_release)
self.fig.canvas.mpl_connect('motion_notify_event', on_motion)
self.handles['axh'] = plt.subplot(gs[0, 1])
self.handles['axh'].set_xlim(-15, 15);
......@@ -451,7 +512,7 @@ class zPlot():
# update f-domain
if self.systemIsStable:
H_f = np.abs(rwth_transforms.iz_Hf(self.f, self.H0, poles, zeroes, poles_order, zeroes_order, True))
H_f = np.abs(rwth_transforms.iz_Hf(self.f, self.H0, poles, zeroes, poles_order, zeroes_order, True)).round(8)
else:
H_f = np.ones(self.f.shape) * -.5
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment