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

- added notebook z-Transformation

parent 4ffdf440
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"jupyter": {
"source_hidden": true
}
},
"outputs": [],
"source": [
"# Copyright 2019 Institut für Nachrichtentechnik, RWTH Aachen University\n",
"%matplotlib widget\n",
"\n",
"\n",
"from ient_nb.ient_signals import *\n",
"from ient_nb.ient_plots import *\n",
"from ient_nb.ient_transforms import *\n",
"\n",
"from src.z_transform.z_transform import zPlot\n",
"\n",
"import matplotlib.pyplot as plt\n",
"import matplotlib.gridspec as gridspec\n",
"import numpy as np"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<div>\n",
" <img src=\"ient_nb/figures/rwth_ient_logo@2x.png\" style=\"float: right;height: 5em;\">\n",
"</div>\n",
"\n",
"# z-Transformation\n",
"\n",
"Zum Starten: Im Menü: Run <span class=\"fa-chevron-right fa\"></span> Run All Cells auswählen."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### plotting pole zero plot in z-plane"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"jupyter": {
"source_hidden": true
}
},
"outputs": [],
"source": [
"n = np.linspace(-5, 5, 11)\n",
"pp = np.array([0.5, 2]); pz = np.array([0])\n",
"roc = np.array([0.5, 2]) \n",
"#roc = np.array([-b, np.inf])\n",
"\n",
"fig, axs = plt.subplots(1, 2)\n",
"ax = axs[0]\n",
"zroc = ient_plot_zroc(ax, roc)\n",
"ax.plot(np.real(pp), np.imag(pp), **ient_style_poles); ax.plot(np.real(pp), -np.imag(pp), **ient_style_poles)\n",
"ax.plot(np.real(pz), np.imag(pz), **ient_style_zeros); ax.plot(np.real(pz), -np.imag(pz), **ient_style_zeros)\n",
"ax.set_xlabel(r'$\\rightarrow \\mathrm{Re}$'); ax.set_ylabel(r'$\\uparrow \\mathrm{Im}$');\n",
"ax.set_xlim(np.min(n), np.max(n)); ax.set_ylim(ax.get_xlim()); ient_grid(ax); ient_axis(ax)\n",
"\n",
"ax = axs[1]\n",
"h_n = ient_iz_ht(n, 1, pp, pz, roc= roc)\n",
"ax.set_xlabel(r'$\\rightarrow \\mathrm{n}$'); ax.set_ylabel(r'$\\uparrow \\mathrm{h(n)}$'); \n",
"ax.set_xlim(np.min(n), np.max(n)); ient_update_ylim(ax, h_n, 0.19, 1e05); ient_grid(ax); ient_axis(ax)\n",
"ient_stem(ax, n, np.real(h_n))\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Interaktive Demo"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"jupyter": {
"source_hidden": true
}
},
"outputs": [],
"source": [
"zp = zPlot()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Anleitung\n",
"Es können vier verschiedene Filter (Butterworth, Sprungfunktion, Sinus und Cosinus) voreingestellt werden. Über den Schieberegler kann der Wert für $H_0$ angepasst werden. Wird der Butterworthfilter gewählt, ist hier der Grad $P$ einstellbar.\n",
"\n",
"Bei *Modus* kann die Position des Konvergenzbereichs geändert werden, indem die entsprechende Option eingestellt und im Pol-/Nullstellendiagramm auf den Bereich geklickt wird, der der neue Konvergenzbereich sein soll. \n",
"\n",
"Ähnlich können auch Pol- und Nullstellen hinzugefügt oder gelöscht werden. Bei *Typ* wird eingestellt, ob es sich um eine Pol- oder Nullstelle handeln soll und unter *Modus* wird dann *hinzufügen* oder *löschen* ausgewählt. Durch Klicken im Pol-/Nullstellendiagramm können nun Pol- und Nullstellen hinzugefügt oder gelöscht werden. Entsprechend ändern sich dann auch die zugehörige Impulsantwort und Übertragungsfunktion.\n",
"\n",
"### Aufgaben\n",
"* Teste verschiedene Einstellungen und ihre Auswirkungen auf Impulsantwort und Übertragungsfunktion. Welche Auswirkung hat das Ändern von $H_0$? Wie sehen die voreingestellten Funktionen aus?\n",
"\n",
"* Entferne alle Pole und Nullstellen. Wie sieht die Impulsantwort aus? Welche Auswirkung hat das Ändern von $H_0$?\n",
"\n",
"* Erstelle eine Polstelle auf der imaginären Achse. Welche Auswirkung hat diese auf die Übertragungsfunktion?\n",
"\n",
"* Erstelle ein Pol-/Nullstellendiagram mit mehreren möglichen Polstellen. Ändere den Konvergenzbereich. Wann existiert eine Übertragungsfunktion?\n",
"\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"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",
"*Emin Kosar, Christian Rohlfing, Übungsbeispiele zur Vorlesung \"Grundgebiete der Elektrotechnik 3 - Signale und Systeme\"*, gehalten von Jens-Rainer Ohm, 2019, 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.7.3"
}
},
"nbformat": 4,
"nbformat_minor": 4
}
%% Cell type:code id: tags:
``` python
# Copyright 2019 Institut für Nachrichtentechnik, RWTH Aachen University
%matplotlib widget
from ient_nb.ient_signals import *
from ient_nb.ient_plots import *
from ient_nb.ient_transforms import *
from src.z_transform.z_transform import zPlot
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
import numpy as np
```
%% Cell type:markdown id: tags:
<div>
<img src="ient_nb/figures/rwth_ient_logo@2x.png" style="float: right;height: 5em;">
</div>
# z-Transformation
Zum Starten: Im Menü: Run <span class="fa-chevron-right fa"></span> Run All Cells auswählen.
%% Cell type:markdown id: tags:
### plotting pole zero plot in z-plane
%% Cell type:code id: tags:
``` python
n = np.linspace(-5, 5, 11)
pp = np.array([0.5, 2]); pz = np.array([0])
roc = np.array([0.5, 2])
#roc = np.array([-b, np.inf])
fig, axs = plt.subplots(1, 2)
ax = axs[0]
zroc = ient_plot_zroc(ax, roc)
ax.plot(np.real(pp), np.imag(pp), **ient_style_poles); ax.plot(np.real(pp), -np.imag(pp), **ient_style_poles)
ax.plot(np.real(pz), np.imag(pz), **ient_style_zeros); ax.plot(np.real(pz), -np.imag(pz), **ient_style_zeros)
ax.set_xlabel(r'$\rightarrow \mathrm{Re}$'); ax.set_ylabel(r'$\uparrow \mathrm{Im}$');
ax.set_xlim(np.min(n), np.max(n)); ax.set_ylim(ax.get_xlim()); ient_grid(ax); ient_axis(ax)
ax = axs[1]
h_n = ient_iz_ht(n, 1, pp, pz, roc= roc)
ax.set_xlabel(r'$\rightarrow \mathrm{n}$'); ax.set_ylabel(r'$\uparrow \mathrm{h(n)}$');
ax.set_xlim(np.min(n), np.max(n)); ient_update_ylim(ax, h_n, 0.19, 1e05); ient_grid(ax); ient_axis(ax)
ient_stem(ax, n, np.real(h_n))
```
%% Cell type:markdown id: tags:
## Interaktive Demo
%% Cell type:code id: tags:
``` python
zp = zPlot()
```
%% Cell type:markdown id: tags:
### Anleitung
Es können vier verschiedene Filter (Butterworth, Sprungfunktion, Sinus und Cosinus) voreingestellt werden. Über den Schieberegler kann der Wert für $H_0$ angepasst werden. Wird der Butterworthfilter gewählt, ist hier der Grad $P$ einstellbar.
Bei *Modus* kann die Position des Konvergenzbereichs geändert werden, indem die entsprechende Option eingestellt und im Pol-/Nullstellendiagramm auf den Bereich geklickt wird, der der neue Konvergenzbereich sein soll.
Ähnlich können auch Pol- und Nullstellen hinzugefügt oder gelöscht werden. Bei *Typ* wird eingestellt, ob es sich um eine Pol- oder Nullstelle handeln soll und unter *Modus* wird dann *hinzufügen* oder *löschen* ausgewählt. Durch Klicken im Pol-/Nullstellendiagramm können nun Pol- und Nullstellen hinzugefügt oder gelöscht werden. Entsprechend ändern sich dann auch die zugehörige Impulsantwort und Übertragungsfunktion.
### Aufgaben
* Teste verschiedene Einstellungen und ihre Auswirkungen auf Impulsantwort und Übertragungsfunktion. Welche Auswirkung hat das Ändern von $H_0$? Wie sehen die voreingestellten Funktionen aus?
* Entferne alle Pole und Nullstellen. Wie sieht die Impulsantwort aus? Welche Auswirkung hat das Ändern von $H_0$?
* Erstelle eine Polstelle auf der imaginären Achse. Welche Auswirkung hat diese auf die Übertragungsfunktion?
* Erstelle ein Pol-/Nullstellendiagram mit mehreren möglichen Polstellen. Ändere den Konvergenzbereich. Wann existiert eine Übertragungsfunktion?
%% Cell type:markdown id: tags:
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).
Please attribute the work as follows:
*Emin Kosar, Christian Rohlfing, Übungsbeispiele zur Vorlesung "Grundgebiete der Elektrotechnik 3 - Signale und Systeme"*, gehalten von Jens-Rainer Ohm, 2019, Institut für Nachrichtentechnik, RWTH Aachen University.
import bisect
from ipywidgets import interact, interactive, fixed, HBox, VBox
import ipywidgets as widgets
from IPython.display import clear_output, display, HTML
from ient_nb.ient_plots import *
from ient_nb.ient_transforms import *
class pzPoint():
h_point = None
h_point_conj = None
h_order = None
def __init__(self, p=0 + 0j, order=1):
# super().__init__(*args, **kwargs)
self.p = p
self.order = order
def clear(self):
if self.h_point is not None: self.h_point.remove(); self.h_point = None
if self.h_point_conj is not None: self.h_point_conj.remove(); self.h_point_conj = None
if self.h_order is not None: self.h_order.remove(); self.h_order = None
class zPlot():
pp = np.array([])
pz = np.array([])
roc = {'sigma': [0, 0], 'zroc' : None, 'inf': 12}
qdelta = 0.1
qthresh = 0.1
fig = None
ax = None
handles = {'ph': None, 'axh': None, 'pH': None, 'axH': None}
# TODO - add more filter types
filter_types = {'Manuell': 'man'}
mode = 'p'
mode_types = {'Polstelle': 'p', 'Nullstelle': 'z'}
action = 'add'
action_locked = False
action_types = {'Hinzufügen': 'add', 'Löschen': 'del', 'Konvergenzbereich ändern': 'roc'}
H = None
Hlog = None
def __init__(self, pp=np.array([0]), pz=np.array([]), ord_p=np.array([1]), ord_z=np.array([])):
self.H0 = 1
#self.filter = 'unit'
self.H0_txt = None
self.length = 31
self.n = np.linspace(-15, 15, num=self.length)
self.f = np.linspace(-15, 15, num=1024)
self.open_figure()
# Poles
return
self.pp = np.array([])
if pp.size != 0 and ord_p.size == 0:
ord_p = np.ones(pp.size)
else:
ord_p = np.array(ord_p)
for i, p in enumerate(pp):
self.update_point(p, 'p', ord_p[i])
# Zeroes
self.pz = np.array([])
if pz.size != 0 and ord_z.size == 0:
ord_z = np.ones(pz.size)
else:
ord_z = np.array(ord_z)
for i, p in enumerate(pz):
self.update_point(p, 'z', ord_z[i])
self.update_roc(1)
def open_figure(self):
fig = plt.figure(figsize=(8, 8))
gs = gridspec.GridSpec(2, 2)
# First axis for plotting s and h
# axes limits and labels
ax = plt.subplot(gs[:, 0])
ax.set_xlim(-6, 6)
ax.set_ylim(-6, 6)
ax.set_xlabel(r'$\rightarrow \mathrm{Re}$')
ax.set_ylabel(r'$\uparrow \mathrm{Im}$')
ient_axis(ax)
self.fig = fig
self.ax = ax
self.ax.set_title('Pol- /Nullstellen Diagramm', fontsize='12')
def onclick(event):
if self.action == 'add' and event.key == 'shift':
self.action = 'del'
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 = 'Löschen'
def onkeyrelease(event):
if not self.action_locked:
self.w_action_type.children[0].value = 'Hinzufügen'
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.handles['axh'] = plt.subplot(gs[0, 1])
self.handles['axh'].set_xlim(-15, 15);
self.handles['axh'].set_title('Impulsantwort', fontsize='12')
self.handles['axh'].set_xlabel(r'$\rightarrow n$')
self.handles['axh'].set_ylabel(r'$\uparrow h(n)$')
self.pbzErrortxt = self.handles['axh'].text(-7, 3.25,
'Zählergrad größer als Nennergrad. \nKeine Partialbruchzerlegung möglich!',
fontsize=12, color='rot', visible=False, bbox=ient_wbbox)
self.handles['containerh'] = ient_stem(self.handles['axh'], self.n, self.n)
ient_axis(self.handles['axh'])
self.handles['axH'] = plt.subplot(gs[1, 1])
self.handles['axH'].set_title('Übertragungsfunktion', fontsize='12')
self.handles['axH'].set_xlabel(r'$\rightarrow f$')
self.handles['axH'].set_ylabel(r'$\uparrow |H(f)|$ [dB]')
self.handles['lineH'], = self.handles['axH'].plot(self.f, self.f)
ient_axis(self.handles['axH'])
self.handles['axH'].spines['bottom'].set_position(('outward', 0))
# initial dirac plot
(self.cp, self.cn) = ient_plot_dirac(self.handles['axh'], [-1, 1], [-1, 1], 'rwth')
ient_dirac_set_data((self.cp, self.cn), [], [])
self.fig.canvas.layout.height = '600px'
fig.tight_layout();
# plt.subplots_adjust(wspace=.25)
# Widgets
self.w_filter_type = interactive(self.update_filter,
filtr=widgets.Dropdown(options=list(self.filter_types.keys()),
value="Manuell", description='Filter'))
self.w_action_type = interactive(self.update_action,
action=widgets.Dropdown(options=list(self.action_types.keys()),
value="Hinzufügen", description='Modus',
disabled=True))
self.w_point_type = interactive(self.update_mode,
mode=widgets.Dropdown(options=list(self.mode_types.keys()), value="Polstelle",
description='Typ', disabled=True))
self.w_amp_type = interactive(self.update_amp, H0=widgets.IntSlider(min=1, max=10, step=1, value=1),
description="H0")
self.w_no_of_poles = interactive(self.update_no_of_poles, P=widgets.IntSlider(min=1, max=7, step=1, value=1),
description="P")
self.VBox_AMP = VBox([self.w_filter_type, self.w_amp_type]) # vertical box for displaying H0
self.VBox_Butter = VBox(
[self.w_filter_type, self.w_no_of_poles]) # vertical box for displaying no. of poles (only for butterworth)
self.VBox_mode_type = VBox([self.w_action_type, self.w_point_type]) # vertical box action type and point type
self.w_hbox = HBox([self.VBox_AMP, self.VBox_mode_type])
display(self.w_hbox)
def find_closest_point(self, p, mode='n'):
if mode == 'n': mode = self.mode
# Check if point is already there
if mode == 'p':
points = self.pp
else:
points = self.pz
ps = np.array(list((x.p for x in points)))
# Search for point by minimizing cost
if ps.size > 0: # some points already available
C1 = np.abs(ps - p) ** 2
C2 = np.abs(ps - p.conjugate()) ** 2 # check also for complex conjugate points
C1min = min(C1, default=-1)
C2min = min(C2, default=-1)
if C1min <= C2min:
ind = np.argmin(C1)
Cmin = C1min
else:
ind = np.argmin(C2)
Cmin = C2min
# self.ax.set_xlabel('xdata=%f, ydata=%f' % (p.real, p.imag))
if mode == 'p':
pPoint = self.pp[ind]
else:
pPoint = self.pz[ind]
else: # no points added yet
Cmin = self.qthresh * 2
ind = -1
if Cmin <= self.qthresh:
pPoint = points[ind]
else:
pPoint = None
return (pPoint, ind)
def update_point(self, p, mode='n', neword=1):
if self.action == 'roc':
self.update_roc(np.abs(p))
return
if mode == 'n': mode = self.mode
# Quantize point to grid and find closest point
p = self.qdelta * round(p.real / self.qdelta) + 1j * self.qdelta * round(p.imag / self.qdelta)
pPoint, ind = self.find_closest_point(p, mode)
# Add new or delete point or update order
if pPoint is None:
if self.action == 'add': # add new point
pPoint = pzPoint(p, neword)
if mode == 'p':
self.pp = np.append(self.pp, pPoint)
ind = self.pp.size - 1
plotstyle = ient_style_poles
else:
self.pz = np.append(self.pz, pPoint)
ind = self.pz.size - 1
plotstyle = ient_style_zeros
# Plot points
pPoint.h_point, = self.ax.plot(p.real, p.imag, **plotstyle)
if np.abs(p.imag) > 0: # plot complex conjugated poles/zeroes
pPoint.h_point_conj, = self.ax.plot(p.real, -p.imag, **plotstyle)
elif self.action == 'del':
return
else: # delete / update order
if self.action == 'add': # increase order
#pPoint.order += 1
pass
elif self.action == 'del':
if pPoint.order > 1: # decrease order
pPoint.order -= 1
else: # delete point
pPoint.clear()
if mode == 'p':
self.pp = np.delete(self.pp, ind)
else:
self.pz = np.delete(self.pz, ind)
# return
# delete(pPoint)
# plot / update order if necessary
if self.action == 'add' or self.action == 'del':
if pPoint.order == 1 and pPoint.h_order is not None:
pPoint.h_order.remove()
pPoint.h_order = None
elif pPoint.order == 2:
if pPoint.h_order is None:
align = 'left' if self.mode == 'p' else 'right'
pPoint.h_order = self.ax.text(pPoint.p.real, abs(pPoint.p.imag), '({:1.0f})'.format(pPoint.order),
fontsize=12, horizontalalignment=align, verticalalignment='bottom')
else:
pPoint.h_order.set_text('({:1.0f})'.format(pPoint.order))
elif pPoint.order > 2:
pPoint.h_order.set_text('({:1.0f})'.format(pPoint.order))
# elif self.action == 'roc':
plt.show()
self.update_roc(np.mean(np.abs(self.roc['sigma'])))
def update_roc(self, p=0):
sigmas = np.sort(np.abs(np.array(list((x.p for x in self.pp)))))
i = bisect.bisect_left(sigmas, p.real)
if i >= sigmas.size:
right = 12
else:
right = sigmas[i]
if i == 0:
left = 0
else:
left = sigmas[i - 1]
self.roc['sigma'][0] = left
self.roc['sigma'][1] = right
xx = np.array([self.roc['sigma'][0], self.roc['sigma'][1]])
if self.roc['zroc'] is None:
self.roc['zroc'] = ient_plot_zroc(self.ax, xx)
else:
self.roc['zroc'].remove()
self.roc['zroc'] = ient_plot_zroc(self.ax, xx)
self.update_plot()
# path = pzp.roc['hatch'].get_paths()[0]
# path.vertices[path.vertices[:,0] == 1,0] = 2
def update_mode(self, mode):
self.mode = self.mode_types[mode]
def update_action(self, action):
self.action = self.action_types[action]
def update_amp(self, H0):
self.H0 = H0
try:
self.H0_txt.remove()
except AttributeError:
pass
self.H0_txt = self.ax.text(4.5, 5, r'$H_0$: ' + str(self.H0), fontsize=12, bbox=ient_wbbox)
self.update_plot()
def update_no_of_poles(self, P):
self.P = P
if self.w_filter_type.kwargs['filtr'] == 'Butterworth': # skip execution on startup
self.update_filter('Butterworth')
def update_filter(self, filtr):
self.filter = self.filter_types[filtr]
poles_butr = np.array([])
ord_poles_butr = np.array([])
# calculate butterworth poles and orders
if filtr == 'Butterworth':
# show slider for no of poles (filtergrad)
self.w_hbox.children = [self.VBox_Butter, self.VBox_mode_type]
filtergrad = self.P
filtergrenzfrequenz = 1 # omega_g = 1
tmp = (np.linspace(0, filtergrad - 1, num=filtergrad) + 0.5) / filtergrad * np.pi + np.pi / 2
tmp = tmp[np.where(tmp <= np.pi)[0]]
poles_butr = np.array(filtergrenzfrequenz * np.exp(1j * tmp))
ord_poles_butr = np.array(np.ones(poles_butr.shape), dtype=int)
else:
# show slider for H0
self.w_hbox.children = [self.VBox_AMP, self.VBox_mode_type]
# clear plot
def clear_all_points():
tmp = list(self.pp) + list(self.pz)
list(map(lambda p: p.clear(), tmp));
self.pp = self.pz = np.array([])
def update_widgets(filtr):
if filtr == "Manuell":
b = False
else:
b = True
self.w_action_type.children[0].disabled = b
self.w_point_type.children[0].disabled = b