Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • IENT/gdet3-demos
1 result
Show changes
Commits on Source (13)
......@@ -2,5 +2,5 @@ ARG BASE_IMAGE=registry.git.rwth-aachen.de/jupyter/profiles/rwth-courses:latest
FROM ${BASE_IMAGE}
RUN conda install --quiet --yes \
'scipy==1.7.1' && \
'scipy==1.7.1' && \
conda clean --all
%% Cell type:code id: tags:
``` python
# Copyright 2020 Institut für Nachrichtentechnik, RWTH Aachen University
%matplotlib widget
from ipywidgets import interact, interactive, fixed, HBox, VBox
import ipywidgets as widgets
from IPython.display import clear_output, display, HTML
import matplotlib.pyplot as plt
from rwth_nb.plots import colors
import rwth_nb.plots.mpl_decorations as rwth_plt
from rwth_nb.misc.signals import *
from rwth_nb.misc.transforms import *
t,deltat = np.linspace(-10,10,50001, retstep=True) # t-axis
f,deltaf = np.linspace(-10,10,len(t), retstep=True) # f-axis
kMax = 16 # number of k values in sum for Sa(f)
```
%% Cell type:markdown id: tags:
<div>
<img src="figures/rwth_ient_logo@2x.png" style="float: right;height: 5em;">
</div>
# Demonstrator Diskrete Fourier-Transformation
Zum Starten: Im Menü: Run <span class="fa-chevron-right fa"></span> Run All Cells auswählen.
## Einleitung
$s(t)$ wird ideal mit Abtastrate $r$ abgetastet.
Diskrete Fourier-Transformation mit $F = \frac{1}{M}$ resultiert in diskreter Fourier-Transformierten
$$
S_\mathrm{d}(k)
=
\sum_{n=0}^{M-1} s_\mathrm{d}(n) \mathrm{e}^{-\mathrm{j}2\pi kFn} \quad k=0,\ldots,M-1 \text{ ,}
$$
wobei $s_\mathrm{d}(n)$ um $M$ periodisches, diskretes Zeitsignal mit $t = n \cdot \frac{1}{r}$.
Für die Rücktransformation in den Zeitbereich gilt dann
$$
s_\mathrm{d}(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{ ,}
$$
wobei
$f = k \cdot \frac{r}{M}$.
## Beispiel
Zeige eine Periode mit $M=24$ Werten
%% Cell type:code id: tags:
``` python
M = 24; # period of discrete signal
r = 4; # sampling rate
n = np.arange(M) # discrete time axis
sd = tri(n/r) + tri((n-M)/r) # discrete signal sd(n)
F = 1/M
# Very slow DFT computation for teaching purposes!
# Fasten your seatbelts with FFF by uncommenting the line with "fft.fft" below
Sd = np.zeros_like(sd, dtype=complex)
for k in np.arange(M):
Sd[k] = np.sum(sd * np.exp(-2j*np.pi*k*F*n))
#Sd = np.fft.fft(sd) # this would be so much faster!
Sd = np.real(Sd) # discard super small imaginary part (1e-16), works "only" for examples like this
# Plot
fig,axs = plt.subplots(2,1)
## Plot discrete time domain
ax = axs[0]; rwth_plt.stem(ax, n, sd);
ax.set_xlabel(r'$\rightarrow n$'); ax.set_ylabel(r'$\uparrow s_\mathrm{d}(n)$')
rwth_plt.axis(ax);
# Plot discrete Fourier domain
ax = axs[1]; rwth_plt.stem(ax, np.arange(M), Sd/r);
ax.set_xlabel(r'$\rightarrow k$'); ax.set_ylabel(r'$\uparrow S_\mathrm{d}(k)/r$')
rwth_plt.axis(ax); fig.tight_layout()
```
%% Cell type:markdown id: tags:
## Demonstrator
Signal $s(t) = \Lambda(t)$ mit zugehörigem Spektrum $S(f) = \mathrm{si}^2(\pi f)$ kann zunächst mit Rate $r$ abgetastet werden. Weiter lässt sich das diskrete Signal periodisch fortsetzen mit Periode $M$.
%% Cell type:code id: tags:
``` python
def decorate_second_axis(ax):
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');
ax.xaxis.set_label_coords(.98,1); ax.xaxis.label.set_horizontalalignment('right')
fig, axs = plt.subplots(2, 1, **rwth_plt.landscape); #fig.tight_layout()
highlight_rect_style = {'facecolor': "none", 'edgecolor': 'rwth:orange', 'linestyle':'--', 'linewidth':2.0}
@widgets.interact( r = widgets.IntSlider(min=0, max=10, step=2, value=0, description='$r$ [Hz]'),
Mbr = widgets.IntSlider(min=0, max=10, step=2, value=0, description='$M/r$'))
def update_plots(r, Mbr):
M = r*Mbr
global n, snT, k, SkF
# Continuous functions s(t) and S(f)
s = tri
S = lambda f: si(np.pi*f) ** 2
# Ideal sampling
Sa_f = np.zeros_like(S(f))
nT = []; snT = nT; n = nT;
if r > 0:
T = 1/r
# Sampled function s(n)
nT, snT = sample(t, s(t), T); n = nT/T;
# Sampled continuous spectrum S_a(f)
for k in np.arange(-kMax, kMax+1): # evaluate infinite sum only for 2*kMax+1 elements
Sa_f += S(f-k/T)
#Sa_f = Sa_f/T # normalize here
# Periodic copies of s(n)
kF = []; SkF = kF; k = kF;
sd = np.zeros_like(snT)
if M > 0:
F = 1/M # in Hz
kF, SkF = sample(f, Sa_f, r*F); k = kF/F/r
for d in np.arange(-kMax, kMax+1): # evaluate infinite sum only for 2*kMax+1 elements
sd += s((n-d*M)*T)
display('M={}'.format(M))
# Plot
if not axs[0].lines: # Call plot() and decorate axes. Usually, these functions take some processing time
ax = axs[0]; ax.set_title('Zeitbereich');
ax.plot(t, s(t), 'k', label='$s(t)$');
ax.set_xlabel(r'$\rightarrow t $ [s]')
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);
axn = ax.twiny(); rwth_plt.stem(axn, [1], [1], color='rwth:blue', label=r'$s(n)$');
axn.set_xlabel(r'$\rightarrow n$ [Samples]'); decorate_second_axis(axn);
axn.legend(loc='upper right', bbox_to_anchor=(1.0, 0.6));
axn.fill_between([-.1, M-1+.1], -.05, 1.05, **highlight_rect_style)
ax = axs[1]; ax.set_title('Frequenzbereich');
ax.plot(f, S(f), 'k', label='$S(f)$')
ax.plot(f, Sa_f, 'rwth:red', label='$S_\mathrm{a}(f)/r$')
ax.set_xlabel(r'$\rightarrow f $ [Hz]')
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);
axk = ax.twiny(); rwth_plt.stem(axk, [1], [1], label=r'$S_\mathrm{d}(k)/r$');
axk.set_xlabel(r'$\rightarrow k$'); decorate_second_axis(axk);
axk.legend(loc='upper right', bbox_to_anchor=(1.0, 0.5));
axk.fill_between([-.1, M-1+.1], -.05, 1.05, **highlight_rect_style)
else: # Update only the plot lines themselves. Should not take too much processing time
ax = axs[0] # time domain
axn = fig.axes[2];
if r > 0:
if len(axn.collections) > 1: axn.collections[1].remove()
if M == 0:
rwth_plt.stem_set_data(axn.containers[0], n, snT); #axn.containers[0].set_label(r'$s(n)$')
else:
rwth_plt.stem_set_data(axn.containers[0], n, sd); #axn.containers[0].set_label(r'$s_\mathrm{d}(n)$')
axn.fill_between([-.1, M-1+.1], -.05, 1.05, **highlight_rect_style)
ax = axs[1] # frequency domain
ax.lines[1].set_ydata(Sa_f)
axk = fig.axes[3];
if M > 0 and r > 0:
if len(axk.collections) > 1: axk.collections[1].remove()
axk.fill_between([-.1, M-1+.1], -.05, 1.05, **highlight_rect_style)
rwth_plt.stem_set_data(axk.containers[0], k, SkF)
axn = fig.axes[2]; axn.set_visible(r > 0)
axk = fig.axes[3]; axk.set_visible(M > 0 and r > 0)
if r > 0:
axn.set_xlim((ax.get_xlim()[0]*r,ax.get_xlim()[1]*r))
if M > 0:
axk.set_xlim((ax.get_xlim()[0]*M/r,ax.get_xlim()[1]*M/r))
```
%% 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:
*Christian Rohlfing, Emin Kosar, Übungsbeispiele zur Vorlesung "Grundgebiete der Elektrotechnik 3 - Signale und Systeme"*, gehalten von Jens-Rainer Ohm, 2021, Institut für Nachrichtentechnik, RWTH Aachen University.
%% Cell type:code id: tags:
``` python
# Copyright 2020 Institut für Nachrichtentechnik, RWTH Aachen University
%matplotlib widget
import ipywidgets as widgets
from ipywidgets import interact, interactive, fixed, Layout
from IPython.display import clear_output, display, HTML
import matplotlib.pyplot as plt
import numpy as np
from scipy import signal # convolution
import rwth_nb.plots.mpl_decorations as rwth_plots
from rwth_nb.misc.signals import *
```
%% Cell type:markdown id: tags:
<div>
<img src="figures/rwth_ient_logo@2x.png" style="float: right;height: 5em;">
</div>
# Demonstrator Faltung
Zum Starten: Im Menü: Run <span class="fa-chevron-right fa"></span> Run All Cells auswählen.
## Einleitung
Im Folgenden wird das Faltungsintegral
$$g(t)
= s(t)\ast h(t)
= \int\limits_{-\infty}^{\infty} s(\tau) h(t-\tau) \,\mathrm{d}\tau
$$
betrachtet.
%% Cell type:markdown id: tags:
## Demo
Wähle $s(t)$ und $h(t)$ sowie jeweils Verschiebung $t_0$ und Dehnungsfaktor $T$ für beide Signale: $s\left(\frac{t-t_0}{T}\right)$ und $h\left(\frac{t-t_0}{T}\right)$.
Zusätzlich zu Elementarsignalen kann auch eine frei definierbare Funktion $s_0(t)$ zur Faltung verwendet werden.
%% Cell type:code id: tags:
``` python
s_0 = lambda t: rect(t/2-1/2)*(-t)
```
%% Cell type:code id: tags:
``` python
def convolution(s, h):
# Convolve s and h numerically
return signal.convolve(s(tau), h(tau), mode='same')*deltat
(tau,deltat) = np.linspace(-15, 15, 10001, retstep=True) # tau axis
interval_cnt = 0; # Interval counter for stepwise plot
# Signals
signal_types = {
'Rechteck' : lambda t: rect(t/1.01), # slightly wider rect than usual, hotfix for stepwise demo
'Dreieck' : tri,
'Sprungfunktion' : unitstep,
'si-Funktion' : lambda t: si(t*np.pi),
'Exponentialimpuls' : lambda t: unitstep(t)*np.exp(-t),
'Gauß-Signal' : gauss,
'Doppelrechteck' : lambda t: rect(t*2+0.5)-rect(t*2-0.5),
'Rampe' : lambda t: t*rect(t-0.5),
'Versch. Rechteck' : lambda t: -rect(t-0.5),
'Eigene Kreation s0(t)' : s_0,
}
# Plot
fig0, axs0 = plt.subplots(1, 2, figsize=(12,2))
def update_signals(s_type, s_T, s_t0, h_type, h_T, h_t0):
# Get s(t) and h(t)
global s, h, gt, intervals_g # reused in second interactive plot
s = lambda tau: signal_types[s_type]((tau-s_t0)/s_T); # signal on time axis (shifted by t0 and stretched by T)
h = lambda tau: signal_types[h_type]((tau-h_t0)/h_T);
#intervals_s = np.array(signal_intervals[w_s_type.value])*s_T + s_t0; # get signal intervals and map to time axis (shifted by t0 and stretched by T)
#intervals_h = np.array(signal_intervals[w_h_type.value])*h_T + h_t0;
intervals_s,_,_ = find_intervals(s(tau), tau, 0.49*np.max(np.abs(s(tau)))/deltat, deltat)
intervals_h,_,_ = find_intervals(h(tau), tau, 0.49*np.max(np.abs(h(tau)))/deltat, deltat)
# Get g(t) = s(t) \ast h(t)
gt = convolution(s, h) # numerical convolution
intervals_g = np.unique(np.tile(intervals_h, len(intervals_s)) + np.repeat(intervals_s, len(intervals_h))) # intervals of g(t)
if intervals_g.size == 0: #
intervals_g = np.concatenate([intervals_s, intervals_h])
# Plot
if not axs0[0].lines: # plot s(t) and g(t)
ax = axs0[0]; ax.plot(tau, s(tau), 'rwth:blue');
ax.set_xlabel(r'$\rightarrow t$'); ax.set_ylabel(r'$\uparrow s(t)$')
ax.set_xlim([-2.9, 2.9]); ax.set_ylim([-1.19, 1.19]); rwth_plots.axis(ax); rwth_plots.grid(ax);
ax = axs0[1]; ax.plot(tau, h(tau), 'rwth:blue');
ax.set_xlabel(r'$\rightarrow t$'); ax.set_ylabel(r'$\uparrow h(t)$')
ax.set_xlim(axs0[0].get_xlim()); ax.set_ylim(axs0[0].get_ylim()); rwth_plots.axis(ax); rwth_plots.grid(ax); fig0.tight_layout();
else: # update lines
axs0[0].lines[0].set_ydata(s(tau));
axs0[1].lines[0].set_ydata(h(tau));
try: # if convolution figure is already opened, update s(tau)
if axs[0].lines:
axs[0].lines[1].set_ydata(s(tau));
rwth_plots.update_ylim(axs[0], np.concatenate((h(tau), s(tau))), 0.19, 5); rwth_plots.update_ylim(axs[1], gt, 0.19, 5);
update_plot(-2) # update convolution plot
update_plot_intervals() # update interval lines
except: pass
rwth_plots.update_ylim(axs0[0], s(tau), 0.19, 5); rwth_plots.update_ylim(axs0[1], h(tau), 0.19, 5);
def swap_signals(b):
s_type = w_s_type.value; h_type = w_h_type.value; w_s_type.value = h_type; w_h_type.value = s_type;
s_T = w_s_T.value; h_T = w_h_T.value; w_s_T.value = h_T; w_h_T.value = s_T;
s_t0 = w_s_t0.value; h_t0 = w_h_t0.value; w_s_t0.value = h_t0; w_h_t0.value = s_t0;
# Widgets
w_s_type = widgets.Dropdown(options=list(signal_types.keys()), description=r'Wähle $s(t)$:')
w_s_T = widgets.FloatSlider(min=0.5, max=4, value=1, step=.1, description=r'Dehnung T', style=rwth_plots.wdgtl_style)
w_s_t0 = s_t0=widgets.FloatSlider(min=-2, max=2, value=0, step=.1, description=r'Verschiebung $t_0$', style=rwth_plots.wdgtl_style)
w_h_type = widgets.Dropdown(options=list(signal_types.keys()), description=r'Wähle $h(t)$:')
w_h_T = widgets.FloatSlider(min=0.5, max=4, value=1, step=.1, description=r'Dehnung T', style=rwth_plots.wdgtl_style)
w_h_t0 = s_t0=widgets.FloatSlider(min=-2, max=2, value=0, step=.1, description=r'Verschiebung $t_0$', style=rwth_plots.wdgtl_style)
w = widgets.interactive(update_signals, s_type=w_s_type, s_T=w_s_T, s_t0=w_s_t0, h_type=w_h_type, h_T=w_h_T, h_t0 = w_h_t0)
display(widgets.HBox((widgets.VBox((w_s_type, w_s_T, w_s_t0)), widgets.VBox((w_h_type, w_h_T, w_h_t0), layout=Layout(margin='0 0 0 100px'))))); w.update();
swap_s_h = widgets.Button(icon='arrows-h', description='Austauschen'); swap_s_h.on_click(swap_signals)
display(widgets.HBox((widgets.VBox((w_s_type, w_s_T, w_s_t0), layout=Layout(margin='0 50px 0 0')),
swap_s_h,
widgets.VBox((w_h_type, w_h_T, w_h_t0), layout=Layout(margin='0 0 0 50px'))))); w.update();
```
%% Cell type:markdown id: tags:
... und betrachte hier das Faltungsergebnis:
%% Cell type:code id: tags:
``` python
fig, axs = plt.subplots(2, 1, figsize=(12, 12/rwth_plots.fig_aspect)) # gridspec_kw = {'width_ratios':[3, 1]}
t_w = widgets.FloatSlider(min=-4, max=4, value=-2, step=.1, description='Verschiebung $t$', style=rwth_plots.wdgtl_style)
intervals_gt = np.array([t_w.min, t_w.max])
@widgets.interact(t=t_w,
show_integrand=widgets.Checkbox(value=True, description='Zeige Integrand', style=rwth_plots.wdgtl_style))
def update_plot(t, show_integrand=True):
global interval_cnt, intervals_gt
t_ind = np.where(tau>=t); t_ind = t_ind[0][0]; g_plot = gt.copy(); g_plot[t_ind:] = np.nan; # hide g(t') with t'>t
sh = s(tau)*h(t-tau) # integrand
interval_cnt = np.argwhere((intervals_gt[1:] > t) & (intervals_gt[:-1] <= t)); # interval
interval_cnt = 0 if interval_cnt.size == 0 else interval_cnt[0][0]; # check for end of interval
if not axs[0].lines: # Call plot() and decorate axes. Usually, these functions take some processing time
ax = axs[0]; ax.plot(tau, h(t-tau), 'rwth:blue', label=r'$h(t-\tau)$'); # plot h(t-tau)
ax.plot(tau, s(tau), 'rwth:green', label=r'$s(\tau)$'); # plot s(tau)
ax.plot(tau, sh, '--', color='rwth:orange', lw=1, label=r'$s(\tau)h(t-\tau)$'); # plot integrand
rwth_plots.annotate_xtick(ax, r'$t$', t, -0.1, 'rwth:blue', 15); # mark t on tau axis
ax.fill_between(tau, 0, sh, facecolor="none", hatch="//", edgecolor='rwth:black', linewidth=0.0); # hatch common area
ax.set_xlabel(r'$\rightarrow \tau$');
ax.set_xlim([-4.2,4.2]); rwth_plots.update_ylim(ax, np.concatenate((h(tau), s(tau))), 0.19, 5);
ax.legend(); rwth_plots.grid(ax); rwth_plots.axis(ax);
ax = axs[1]; ax.plot(tau, g_plot); # plot g(t)
ax.plot([t, t], [0, gt[t_ind]], 'ko--', lw=1);
ax.set_xlabel(r'$\rightarrow t$'); ax.set_ylabel(r'$\uparrow g(t)=s(t)\ast h(t)$');
ax.set_xlim(axs[0].get_xlim()); rwth_plots.update_ylim(ax, gt, 0.19, 5);
rwth_plots.grid(ax); rwth_plots.axis(ax); fig.tight_layout(); #plt.subplots_adjust(wspace=.1,hspace=.1)
else: # Replace only xdata and ydata since plt.plot() takes longer time
ax = axs[0]; ax.lines[0].set_ydata(h(t-tau)); ax.lines[2].set_ydata(sh); # update signals
ax.texts[0].set_x(t); ax.lines[3].set_xdata([t,t]) # update labels
if ax.collections: ax.collections[0].remove(); # update integrand
if show_integrand: ax.fill_between(tau,0,sh, facecolor="none", hatch="//", edgecolor='k', linewidth=0.0);
ax = axs[1]; ax.lines[0].set_ydata(g_plot); # update signals
ax.lines[1].set_xdata([t, t]); ax.lines[1].set_ydata([0, gt[t_ind]]); # update labels
axs[0].lines[2].set_visible(show_integrand)
ax_intervals = axs[1].twinx(); ax_intervals.set_visible(False)
def update_plot_intervals():
# Update interval lines
ax_intervals.clear(); ax_intervals.axis('off');
for x in intervals_g:
ax_intervals.axvline(x, color='rwth:red',linestyle='--',lw=1)
try:
toggle_stepwise(stepwise.value)
except: pass
update_plot_intervals()
```
%% Cell type:markdown id: tags:
Verschiebung $t$ kann automatisch abgespielt werden. Eine schrittweise Betrachtung ist ebenfalls möglich.
%% Cell type:code id: tags:
``` python
def update_t_slider(ti):
global interval_cnt
if interval_cnt > len(intervals_gt)-2:
interval_cnt = 0
tmin = intervals_gt[interval_cnt]; tmax = intervals_gt[interval_cnt+1];
t_w.value = tmin + ti/100*(tmax-tmin) # Update float slider
def reset_t_slider(b):
global interval_cnt
play._playing = False
interval_cnt = 0
intslider.value = 0
stepwise.value = False
update_t_slider(0)
def update_stepwise(args):
visible = args['new']
toggle_stepwise(visible)
def toggle_stepwise(visible):
global intervals_gt
ax_intervals.set_visible(visible)
if visible:
intervals_gt = np.hstack([t_w.min, intervals_g, t_w.max ])
else:
intervals_gt = np.array([t_w.min, t_w.max])
# Widget
play = widgets.Play(value=0, min=0, max=100,step=10, description="Press play", show_repeat=False)
stepwise =widgets.Checkbox(value=False, description='Schrittweise')
reset = widgets.Button(description="Reset"); reset.on_click(reset_t_slider);
intslider = widgets.IntSlider(description='Fortschritt') # Dummy slider in integer format, to be mapped to float slider
status = widgets.Label(value='Nothing')
widgets.jslink((play, 'value'), (intslider, 'value')) # Link dummy slider to player
#def update_t_limits(args):
# status.value = str(np.round(intslider.value)) + " " + str(t_w.value) + " " + str(interval_cnt) + " " + str(args['old']) + " " + str(args['new']);
#play.observe(update_t_limits, '_playing')
stepwise.observe(update_stepwise, 'value')
widgets.interactive(update_t_slider, ti = intslider); intslider.layout.display = 'none';
widgets.HBox([play, stepwise, reset, intslider])#, status
```
%% Cell type:markdown id: tags:
Verschiebung $t$ kann automatisch abgespielt werden. Eine schrittweise Betrachtung ist ebenfalls möglich.
%% Cell type:markdown id: tags:
### Aufgaben:
* Bewege den Schieberegler für $t$ und betrachte das entstehende Faltungsintegral. Wie sind die zugehörigen Integralsgrenzen und welche Intervalle (vgl. Notebook zur Faltung) sind zu beobachten?
* Wähle zwei Rechtecke unterschiedlicher Breite aus. Wie sieht das entstehende Signal aus? Wie breit ist es? Was passiert, wenn eins der Rechtecke um $t_0$ verschoben wird?
* Welche Höhe bei $t=0$ hat das Resultat der Faltung $g(t) = \mathrm{rect}\left(\frac{t}{2}\right)\ast \mathrm{rect}\left(\frac{t}{2}\right)$? Überprüfe die Überlegungen mit Hilfe der entsprechenden Funktionen in der Demo.
* Gilt das Kommutativgesetz $s(t) \ast h(t) = h(t) \ast s(t)$?
* Wie sieht das Faltungsergebnis zweier si-Funktionen aus? Wie das Ergebnis zweier Gaußfunktionen?
* Reale Rechteckimpulse weisen nur eine endliche Flankensteilheit auf. Diese können beispielsweise mit $s(t)=\mathrm{rect}(t)*\mathrm{rect}(t/T)$ oder $s(t)=\mathrm{rect}(t)*\Lambda(\frac{t}{T})$ beschrieben werden. Betrachte diese Fälle für $|T|<\frac{1}{2}$. Wie hängen Gesamtdauer und Dauer der Anstiegsflanke von $T$ ab?
%% 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:
*Christian Rohlfing, Übungsbeispiele zur Vorlesung "Grundgebiete der Elektrotechnik 3 - Signale und Systeme"*, gehalten von Jens-Rainer Ohm, 2020, Institut für Nachrichtentechnik, RWTH Aachen University.
......
%% Cell type:code id: tags:
``` python
# Copyright 2020 Institut für Nachrichtentechnik, RWTH Aachen University
%matplotlib widget
from ipywidgets import interact, interactive
import ipywidgets as widgets
from IPython.display import clear_output, display, HTML
import matplotlib.pyplot as plt
import numpy as np
from scipy import signal
import rwth_nb.plots.mpl_decorations as rwth_plots
from rwth_nb.misc.signals import *
# tau-axis
(tau,deltat) = np.linspace(-20, 20, 4001, retstep=True)
```
%% Cell type:markdown id: tags:
<div>
<img src="figures/rwth_ient_logo@2x.png" style="float: right;height: 5em;">
</div>
# Beispiel zur Berechnung des Faltungsintegrals
Zum Starten: Im Menü: Run <span class="fa-chevron-right fa"></span> Run All Cells auswählen.
## Einleitung
Das Faltungsintegral ist definiert als
$$g(t)
= s(t)\ast h(t)
= \int\limits_{-\infty}^{\infty} s(\tau) h(t-\tau) \mathrm{d}\tau\text{ .}$$
%% Cell type:markdown id: tags:
## Herleitung
### Eingangssignal und Impulsantwort
Wie im Abschnitt 1.6 des Skripts gezeigt, wird hier die Faltung anhand der Impulsantwort des $RC$-Systems
$$h(t) = \frac{1}{T}\varepsilon(t)\mathrm{e}^{-t/T}$$
mit $T=RC$ gezeigt. Es soll dessen Reaktion auf ein Rechteckimpuls der Dauer $T_0$ und der Amplitude $a$
$$s(t) = a\;\mathrm{rect}\left(\frac{t-T_0/2}{T_0}\right)$$
untersucht werden. Beide Funktionen sind in der folgenden Abbildung dargestellt.
%% Cell type:code id: tags:
``` python
# Input signal
T0 = 4 # duration of rect
a = 1/4 # amplitude of rect
s = lambda t: a*rect((t-T0/2)/T0) # Rect impuls with duration T0 and amplitude a
# Impulse response h(t) of RC system
T = 2
h = lambda t: 1/T*unitstep(t)*np.exp(-t/T) # RC system with T=R*C
```
%% Cell type:code id: tags:
``` python
# Plot both signals
fig,ax = plt.subplots(1,1); ax.plot(tau, h(tau), 'rwth:blue'); ax.plot(tau, s(tau), 'rwth:green');
ax.set_xlabel(r'$\rightarrow \tau$'); ax.set_xlim([-11,11]); ax.set_ylim([-0.05,0.55]); rwth_plots.axis(ax);
ax.text(T/4, 1/T, r'$h(\tau)$', color='rwth:blue', fontsize=12);
ax.text(T0, a, r'$s(\tau)$', color='rwth:green', fontsize=12);
```
%% Cell type:markdown id: tags:
### Herleitung Faltungsintegral
%% Cell type:markdown id: tags:
$h(\tau)$ wird zunächst gespiegelt, das resultierende Signal ist $h(-\tau)$.
%% Cell type:code id: tags:
``` python
fig,ax = plt.subplots(1,1); ax.plot(tau, h(-tau), 'rwth:blue');
ax.set_xlabel(r'$\rightarrow \tau$'); ax.set_ylabel(r'$\uparrow h(-\tau)$');
ax.set_xlim([-11,11]); ax.set_ylim([-0.05,0.55]); rwth_plots.axis(ax);
```
%% Cell type:markdown id: tags:
Die gespiegelte Version $h(-\tau)$ wird um $t$ verschoben, wobei $t$ sowohl positive als auch negative Werte annehmen kann.
%% Cell type:code id: tags:
``` python
fig,ax = plt.subplots(1,1);
t = 2
ax.plot(tau, h(t-tau), 'rwth:blue');
rwth_plots.annotate_xtick(ax, r'$t>0$', t, -0.05, 'rwth:blue')
t = -3
line_h2,= ax.plot(tau, h(t-tau), 'rwth:red');
rwth_plots.annotate_xtick(ax, r'$t<0$', t, -0.05, 'rwth:red')
ax.set_xlim([-11,11]); ax.set_ylim([-0.09,0.55]);
ax.set_xlabel(r'$\rightarrow \tau$'); ax.set_ylabel(r'$\uparrow h(t-\tau)$'); rwth_plots.axis(ax);
```
%% Cell type:markdown id: tags:
### Intervall I: $t<0$
Betrachtet man nun $s(\tau)$ und $h(t-\tau)$ für $t<0$ gemeinsam, wird klar, dass in diesem Beispiel für den Integranden des Faltungsintegrals gilt:
$$s(\tau)h(t-\tau)=0$$ und daher $$g(t)=0 \qquad \text{für } t<0 \text{.}$$
Dies gilt für alle Bereiche, in denen keine Überlappung der beiden Signale besteht.
%% Cell type:code id: tags:
``` python
t = -3
fig,ax = plt.subplots(1,1); ax.plot(tau, h(t-tau), 'rwth:blue'); ax.plot(tau, s(tau), '-.', color='rwth:green');
rwth_plots.annotate_xtick(ax, r'$t<0$', t, -0.05, 'rwth:blue');
ax.set_xlabel(r'$\rightarrow \tau$'); ax.set_ylabel(r'$\uparrow h(t-\tau)$');
ax.set_xlim([-11,11]); ax.set_ylim([-0.09,0.55]); rwth_plots.axis(ax);
```
%% Cell type:markdown id: tags:
### Intervall II: $0<t\leq T_0$
Nun taucht $h(t-\tau)$ in $s(\tau)$ ein, und zwar für Zeiten $0<t\leq T_0$:
%% Cell type:code id: tags:
``` python
t = T0/2 # ensure 0<t<=T0
fig,ax = plt.subplots(1,1); ax.plot(tau, h(t-tau), 'rwth:blue'); ax.plot(tau, s(tau), '-.', color='rwth:green');
mask = (tau>0) & (tau<t);
ax.fill_between(tau[mask],0,h(t-tau[mask]), facecolor="none", hatch="//", edgecolor='rwth:black', linewidth=0.0);
rwth_plots.annotate_xtick(ax, r'$0<t\leq T_0$', t, -0.075, 'rwth:blue');
rwth_plots.annotate_xtick(ax, r'$T_0$', T0, -0.125, 'rwth:green');
ax.set_xlabel(r'$\rightarrow \tau$'); ax.set_ylabel(r'$\uparrow h(t-\tau)$');
ax.set_xlim([-11,11]); ax.set_ylim([-0.19,0.55]); rwth_plots.axis(ax);
```
%% Cell type:markdown id: tags:
In diesem Fall ($0<t\leq T_0$) ist das Produkt $s(\tau)h(t-\tau)$ nur im Intervall $0<\tau <t$ von Null verschieden. Dieses Intervall begrenzt nun das Faltungsintegral
$$g(t) = \int\limits_0^t s(\tau)h(t-\tau) \mathrm{d}\tau = a \left(1-\mathrm{e}^{-t/T}\right)\qquad \text{für } 0<t\leq T_0\text{.}$$
Mehr Zwischenschritte werden im Skript gezeigt.
### Intervall III: $t>T_0$
Für alle Zeiten $t>T_0$ liegt $s(\tau)$ komplett in $h(t-\tau)$:
%% Cell type:code id: tags:
``` python
t = T0 + 2 # ensure t>T0
fig,ax = plt.subplots(1,1); ax.plot(tau, h(t-tau), 'rwth:blue'); ax.plot(tau, s(tau), '-.', color='rwth:green');
mask = (tau>0) & (tau<T0);
ax.fill_between(tau[mask],0,h(t-tau[mask]), facecolor="none", hatch="//", edgecolor='rwth:black', linewidth=0.0);
rwth_plots.annotate_xtick(ax, r'$t>T_0$', t, -0.075, 'rwth:blue');
rwth_plots.annotate_xtick(ax, r'$T_0$', T0, -0.125, 'rwth:green');
ax.set_xlabel(r'$\rightarrow \tau$'); ax.set_ylabel(r'$\uparrow h(t-\tau)$');
ax.set_xlim([-11,11]); ax.set_ylim([-0.19,0.55]); rwth_plots.axis(ax);
```
%% Cell type:markdown id: tags:
In diesem Fall ($t>T_0$) ist das Produkt $s(\tau)h(t-\tau)$ in dem festen Intervall $0<\tau<T_0$, welches von $s(\tau)$ bestimmt wird, von Null verschieden
$$g(t) = \int\limits_0^{T_0} s(\tau)h(t-\tau) \mathrm{d}\tau = a \left(\mathrm{e}^{T_0/T}-1\right)\mathrm{e}^{-t/T}\quad \text{für } t>T_0\text{.}$$
%% Cell type:markdown id: tags:
### Betrachtung aller Intervalle gemeinsam
Insgesamt lässt sich $g(t)$ nun wie folgt darstellen:
%% Cell type:code id: tags:
``` python
def g(t):
out = np.zeros_like(t) # initialize with zeros, covers first interval t<0
# Second interval 0<t<= T0 (cropped with rect(t/T0-1/2))
out += a*(1-np.exp(-t/T))*rect(t/T0-1/2)
# Third interval t>T0 (cropped with unitstep(t-T0))
out += a*(np.exp(T0/T)-1)*np.exp(-t/T)*unitstep(t-T0)
return out
t = tau # now we need the whole t axis
# Plot both s(t) and g(t)
fig,ax = plt.subplots(1,1, figsize=(8,4));
ax.plot(t, g(t)/a, 'rwth:blue');
ax.plot(t, s(t)/a,'-.', color='rwth:green',zorder=1);
rwth_plots.annotate_xtick(ax, r'$T_0$', T0, -0.125, 'rwth:green');
ax.set_xlabel(r'$\rightarrow t$'); ax.set_ylabel(r'$\uparrow g(t)/a$');
ax.set_xlim([-6,16]); ax.set_ylim([-0.15,1.19]); rwth_plots.axis(ax);
ax.text(-5,0.5,'Intervall I', color='rwth:black-50', fontsize=12);
ax.text(2.5,0.5,'II', color='rwth:black-50', fontsize=12); ax.text(7.5,0.5,'III', color='rwth:black-50', fontsize=12);
```
%% Cell type:markdown id: tags:
### Interaktive Demo
%% Cell type:code id: tags:
``` python
fig, axs = plt.subplots(2, 1, figsize=(8,6))
fig, axs = plt.subplots(2, 1, figsize=(8,6)); fig.tight_layout();
gt = g(t)
@widgets.interact(t=widgets.FloatSlider(min=-5, max=15, step=.2, value=5,description='$t$', continuous_update=True))
def update_plot(t):
t_ind = np.where(tau>=t); t_ind = t_ind[0][0]; # find index corresponding to t
g_plot = gt.copy()/a; g_plot[t_ind:] = np.nan; # cropped g(t)
mask = (tau>0) & (tau<t) & (tau < T0); # mask for hatch
if not axs[0].lines: # call plot() and decorate axes. Usually, these functions take some processing time
ax = axs[0]; line_h,=ax.plot(tau,h(t-tau), 'rwth:blue'); # plot h(t-tau)
rwth_plots.annotate_xtick(ax, r'$t$', t, -0.075, line_h.get_color(), 14);
ax.plot(tau, s(tau),'-.',color='rwth:green', zorder=1)
ax.fill_between(tau[mask],0,h(t-tau[mask]), facecolor="none", hatch="//", edgecolor='k', linewidth=0.0);
ax.set_xlabel(r'$\rightarrow \tau$'); ax.set_ylabel(r'$\uparrow h(t-\tau)$');
ax.set_xlim([-6,16]); ax.set_ylim([-.19,.65]); rwth_plots.axis(ax)
ax = axs[1]; ax.plot(tau,g_plot, 'rwth:blue'); # plot g(t)
ax.plot([t, t], [0, gt[t_ind]/a], 'ko--', lw=1);
ax.set_xlabel(r'$\rightarrow t$'); ax.set_ylabel(r'$\uparrow g(t)/a$');
ax.set_xlim([-6,16]); ax.set_ylim([-.15,1.19]);rwth_plots.axis(ax)
else: # If lines exist, replace only xdata and ydata since plt.plot() takes longer time
axs[0].lines[0].set_ydata(h(t-tau));
axs[0].texts[0].set_x(t); axs[0].lines[1].set_xdata([t,t])
axs[0].collections[0].remove();
axs[0].fill_between(tau[mask],0,h(t-tau[mask]), facecolor="none", hatch="//", edgecolor='k', linewidth=0.0);
axs[1].lines[0].set_ydata(g_plot);
axs[1].lines[1].set_xdata([t, t]); axs[1].lines[1].set_ydata([0, gt[t_ind]/a]);
```
%% Cell type:markdown id: tags:
Verschiebe den Schieberegler langsam von links nach rechts. Beobachte die oben beschriebenen Intervalle und ihre Grenzen. An welchen Stellen ändert sich das Verhalten von $g(t)$?
Eine ausführlichere Demo zur Faltung mit einer Auswahl für $s(t)$ und $h(t)$ findet man [hier](GDET3%20Faltung%20GUI.ipynb).
%% 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:
*Christian Rohlfing, Übungsbeispiele zur Vorlesung "Grundgebiete der Elektrotechnik 3 - Signale und Systeme"*, gehalten von Jens-Rainer Ohm, 2020, Institut für Nachrichtentechnik, RWTH Aachen University.
......
%% Cell type:code id: tags:
``` python
# Copyright 2020 Institut für Nachrichtentechnik, RWTH Aachen University
%matplotlib widget
from ipywidgets import interact, interactive
import ipywidgets as widgets
from IPython.display import clear_output, display, HTML
import matplotlib.pyplot as plt
import numpy as np
import cmath # for sqrt(-1)
import rwth_nb.plots.mpl_decorations as rwth_plots
from rwth_nb.misc.signals import *
plt.close('all')
```
%% Cell type:markdown id: tags:
<div class="inline-block">
<img src="figures/rwth_ient_logo@2x.png" style="float: right;height: 5em;">
</div>
# RLC-System
Zum Starten: Im Menü: Run <span class="fa-chevron-right fa"></span> Run All Cells auswählen.
%% Cell type:markdown id: tags:
## Einleitung
Es wird folgendes RLC-System betrachtet:
![Blockdiagramm](figures/rlc_system_block_diagram.png)
%% Cell type:code id: tags:
``` python
# Exemplary values
R = 16 # Ohm
R = 16 # Ohm, Ω
L = 1.5E-3 # Henry, mH
C = 1E-6 # Farad, myF
```
%% Cell type:markdown id: tags:
## Berechnung Laplace-Übertragungsfunktion
Die Übertragungsfunktion des Systems kann im Laplace-Bereich mittels Spannungsteiler berechnet werden:
$$
H(p)
=
\frac{U_2(p)}{U_1(p)}
=
\frac{1/(Cp)}{R+Lp+1/(Cp)}
=
\frac{1}{LCp^2+RCp+1}
=
\frac{1/(LC)}{p^2+(R/L)p + 1/(LC)}
$$
mit $p = \sigma + \mathrm{j}\omega = \sigma + \mathrm{j}2 \pi f$.
%% Cell type:code id: tags:
``` python
# Laplace transfer function
H_laplace = lambda p: 1/(L*C*p**2+R*C*p+1);
```
%% Cell type:markdown id: tags:
### Polstellen
Die zugehörigen Polstellen berechnen sich zu
$$p_{\mathrm{P},1,2} =
- \underbrace{\frac{R}{2L}}_{=a} \pm \underbrace{\sqrt{\frac{R^2}{4L^2} - \frac{1}{LC}}}_{=b}
$$
mit $a=\frac{R}{2L}$ und $b=\sqrt{\frac{R^2}{4L^2}-\frac{1}{LC}}$.
%% Cell type:code id: tags:
``` python
# Poles
a = R/(2*L)
b = cmath.sqrt(R**2/(4*L**2)-1/(L*C))
p_p1 = -a+b
p_p2 = -a-b
# Print out the numbers
print("a={0:.3f}".format(a))
print("b={0:.3f}".format(b))
if R**2/(4*L**2) >= 1/(L*C): print("b reell")
else: print("b imaginär")
print("\nPolstellen:")
print("p_p1={0:.3f}, p_p2={0:.3f}".format(p_p1, p_p2))
```
%% Cell type:markdown id: tags:
### Pol-Nulstellendiagramm
Nachfolgend wird das Pol-Nulstellendiagramm geplottet. Es enthält die beiden konjugiert komplexen Polstellen, den Konvergenzbereich und das zugehörige $H_0$.
Da der Konvergenzbereich die imaginäre Achse beinhaltet, ist das System stabil.
%% Cell type:code id: tags:
``` python
beta = np.imag(b) # Imaginary part of the poles
pp = np.array([p_p1, p_p2]); pz = np.array([]) # Zeros # Poles and Zeros
ord_p = np.array([1, 1]); ord_z = np.array([]) # Poles' and Zeros' orders
roc = np.array([np.max(np.real(pp)), np.inf]) # region of convergence
# Plot
fig, ax = plt.subplots()
ax.plot(np.real(pp), np.imag(pp), **rwth_plots.style_poles); ax.plot(np.real(pp), -np.imag(pp), **rwth_plots.style_poles); rwth_plots.annotate_order(ax, pp, ord_p);
ax.plot(np.real(pz), np.imag(pz), **rwth_plots.style_zeros); ax.plot(np.real(pz), -np.imag(pz), **rwth_plots.style_zeros); rwth_plots.annotate_order(ax, pz, ord_z);
rwth_plots.plot_lroc(ax, roc, 500, np.imag(p_p1)); ax.text(-1000,ax.get_ylim()[1]*0.8,'$H_0 = 1/(LC)$',bbox = rwth_plots.wbbox);
ax.set_xlim(ax.get_xlim()[0], 300); rwth_plots.annotate_xtick(ax, r'$-a$', -a,0,'k');
ax.set_xlabel(r'$\rightarrow \mathrm{Re}\{p\}$'); ax.set_ylabel(r'$\uparrow \mathrm{Im}\{p\}$'); rwth_plots.grid(ax); rwth_plots.axis(ax);
```
%% Cell type:markdown id: tags:
## Fourier-Übertragungsfunktion
Aus der Laplaceübertragungsfunktion kann die Fourierübertragungsfunktion berechnet werden, indem $p = \mathrm{j}2\pi f$ gesetzt wird:
$$H(p = \mathrm{j}2\pi f) \quad\text{mit}\quad \omega_0 = \frac{1}{\sqrt{LC}}$$
%% Cell type:code id: tags:
``` python
# Fourier transfer function
H_fourier = lambda f: H_laplace(1j*2*np.pi*f)
f = np.linspace(0, 10000, 10000) # frequency axis
# Resonance frequency
omega0 = 1/np.sqrt(L*C)
f0 = omega0/(2*np.pi)
# Print f0
print("f0 = {0:.2f} Hz".format (f0))
# Plot
fig,ax = plt.subplots()
ax.plot(f/1000, np.abs(H_fourier(f))); rwth_plots.axis(ax)
ax.set_xlabel(r'$\rightarrow f$ [kHz]'); ax.set_ylabel(r'$\uparrow |H(f)|$');
rwth_plots.annotate_xtick(ax, r'$f_0 = \omega_0/(2\pi)$', omega0/(2*np.pi)/1000,-0.25,'rwth:black');
ax.axvline(f0/1000,0,1, color='k',linestyle='--');
```
%% Cell type:markdown id: tags:
## Impulsantwort
Die Impulsantwort kann mittels Partialbruchzerlegung (siehe Vorlesung) bestimmt werden zu
$$
h(t) = \frac{\mathrm{e}^{-at}}{bLC}
\frac{\mathrm{e}^{bt}-\mathrm{e}^{-bt}}{2}
\cdot \varepsilon(t)
$$
mit $a=\frac{R}{2L}$ und $b=\sqrt{\frac{R^2}{4L^2}-\frac{1}{LC}}$.
Der nachfolgende Plot zeigt diese Impulsantwort.
%% Cell type:code id: tags:
``` python
# Impulse response (time-domain)
h = lambda t: np.real(np.exp(-a*t)/(b*L*C)*(np.exp(b*t)-np.exp(-b*t))/2*unitstep(t))
t = np.linspace(-0.001, 0.01, 10000) # time axis
# Plot
fig,ax = plt.subplots()
ax.plot(t*1000, h(t), 'rwth:blue'); rwth_plots.axis(ax); ax.set_xlim([-0.1, 5])
ax.set_xlabel(r'$\rightarrow t$ [ms]'); ax.set_ylabel(r'$\uparrow h(t)$');
#np.mean(np.abs(h(t) - 1/(beta*L*C)*np.exp(-a*t)*np.sin(beta*t)*unitstep(t))**2)
```
%% Cell type:markdown id: tags:
## Interaktive Demo
In dieser interaktiven Demo kann das Verhalten des Systems für variable Werte von $R$ betrachtet werden. Über den Schieberegler kann der Wert für $R$ geändert werden, entsprechend sieht man die Änderungen für die Fourier-Übertragungsfunktion und die Impulsantwort.
%% Cell type:code id: tags:
``` python
fig0,axs = plt.subplots(2, 1, **rwth_plots.landscape)
@widgets.interact(Rsel=widgets.FloatSlider(min=0, max=200, step=1, value=R, description='$R$ [$\Omega$]'))
def update_plot(Rsel):
H_laplace = lambda p: 1/(L*C*p**2+Rsel*C*p+1);
H_fourier = lambda f: H_laplace(1j*2*np.pi*f);
a = Rsel/(2*L)
b = cmath.sqrt(Rsel**2/(4*L**2)-1/(L*C))
h = lambda t: np.real(np.exp(-a*t)/(b*L*C)*(np.exp(b*t)-np.exp(-b*t))/2*unitstep(t))
if not axs[0].lines: # Call plot() and decorate axes. Usually, these functions take some processing time
ax = axs[0]; ax.plot(f/1000, np.abs(H_fourier(f)), 'rwth:blue'); rwth_plots.axis(ax)
ax.set_xlabel(r'$\rightarrow f$ [kHz]'); ax.set_ylabel(r'$\uparrow |H(f)|$');
ax.axvline(f0/1000,0,1, color='rwth:black',linestyle='--');
ax = axs[1]; ax.plot(t*1000, h(t), 'rwth:blue'); rwth_plots.axis(ax); ax.set_xlim([-.225, 5])
ax.set_xlabel(r'$\rightarrow t$ [ms]'); ax.set_ylabel(r'$\uparrow h(t)$');
else: # If lines exist, replace only xdata and ydata since plt.plot() takes longer time
axs[0].lines[0].set_ydata(np.abs(H_fourier(f)))
axs[1].lines[0].set_ydata(h(t))
tmp = np.max(np.abs(H_fourier(f))); rwth_plots.update_ylim(axs[0], np.abs(H_fourier(f)), 0.1*tmp, tmp+10 )
tmp = np.max(np.abs(h(t))); rwth_plots.update_ylim(axs[1], (h(t)), 0.1*tmp, tmp+10 )
display(HTML('{}<br />{}'.format('a={0:.3f}'.format(a), 'b={0:.3f}'.format(b))))
```
%% Cell type:markdown id: tags:
### Experiment
Mit $R=0 \,\Omega$, $L=47\,\mathrm{mH}$ und $C=0,68\,\mu\mathrm{F}$ liegt die entsprechende Grenzfrequenz bei $f_0 \approx 900\,Hz$. Rauchentwicklung ab Sekunde 20 ;)
%% Cell type:code id: tags:
``` python
from IPython.display import Video
Video("figures/RLC_Experiment.mp4", width=480, height=270 )
```
%% 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:
*Christian Rohlfing, Übungsbeispiele zur Vorlesung "Grundgebiete der Elektrotechnik 3 - Signale und Systeme"*, gehalten von Jens-Rainer Ohm, 2020, Institut für Nachrichtentechnik, RWTH Aachen University.
*Christian Rohlfing, Übungsbeispiele zur Vorlesung "Grundgebiete der Elektrotechnik 3 - Signale und Systeme"*, gehalten von Jens-Rainer Ohm, 2021, Institut für Nachrichtentechnik, RWTH Aachen University.
......
......@@ -2,6 +2,7 @@
# Übungsbeispiele zur Vorlesung Grundgebiete der Elektrotechnik 3 &ndash; Signale und Systeme
[![RWTHjupyter](https://jupyter.pages.rwth-aachen.de/documentation/images/badge-launch-rwth-jupyter.svg)](https://jupyter.rwth-aachen.de/hub/spawn?profile=gdet3&next=/user-redirect/lab/tree/gdet3%2Findex.ipynb)
[![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/git/https%3A%2F%2Fgit.rwth-aachen.de%2FIENT%2Fgdet3-demos/master?urlpath=lab/tree/index.ipynb)
## Introduction
......@@ -12,10 +13,10 @@ Visit the notebook [index.ipynb](index.ipynb) for a table of contents.
## Quick Start
Run the notebooks directly online [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/git/https%3A%2F%2Fgit.rwth-aachen.de%2FIENT%2Fgdet3-demos/master?urlpath=lab/tree/index.ipynb). Please note the following limitations:
Run the notebooks directly online with either [![RWTHjupyter](https://jupyter.pages.rwth-aachen.de/documentation/images/badge-launch-rwth-jupyter.svg)](https://jupyter.rwth-aachen.de/hub/spawn?profile=gdet3&next=/user-redirect/lab/tree/gdet3%2Findex.ipynb) or [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/git/https%3A%2F%2Fgit.rwth-aachen.de%2FIENT%2Fgdet3-demos/master?urlpath=lab/tree/index.ipynb). Please note the following limitations:
* The starting process of the session may take up to one minute.
* Please note that the session will be cancelled after 10 minutes of user inactivity.
* Please note that the Binder session will be cancelled after 10 minutes of user inactivity.
## Usage
......@@ -48,14 +49,16 @@ To run the notebooks on your local machine, you may use [Anaconda](https://www.a
### Docker
For advanced users only: If you happen to have Docker installed, you can start a local dockerized JupyterLab with enabled GDET3-Demos with
For advanced users only: If you happen to have Docker installed, you can start a local dockerized JupyterLab
```bash
docker run --name='gdet3-demos' --rm -it -p 8888:8888 -e JUPYTER_ENABLE_LAB=yes registry.git.rwth-aachen.de/ient/gdet3-demos:master
git clone --recurse-submodules https://git.rwth-aachen.de/IENT/gdet3-demos.git
docker run --name='gdet3-demos' --rm -it -p 8888:8888 -v ${pwd}/gdet3-demos:/home/jovyan/work registry.git.rwth-aachen.de/ient/gdet3-demos:master
```
Copy and paste the displayed link to your favorite browser.
## Contact
* If you found a bug, please use the [issue tracker](https://git.rwth-aachen.de/IENT/gdet3-demos/issues).
......
File added
%% Cell type:markdown id: tags:
<div>
<img src="figures/rwth_ient_logo@2x.png" style="float: right;height: 5em;">
</div>
# Übungsbeispiele zu Grundgebiete der Elektrotechnik 3 &ndash; Signale und Systeme
## Jupyter Quick Start
* Zum Ausführen aller Cells eines Notebooks: Im Menü: Run <span class="fa-chevron-right fa"></span> Run All Cells (oder <span class="fa-forward fa"></span>-Button)
* Zum Neustart: <span class="fa-refresh fa"></span>-Button
* Zum Ausführen einer einzelnen Cell: <span class="fa-play fa"></span>-Button
## Inhaltsverzeichnis
%% Cell type:markdown id: tags:
1. Determinierte Signale in linearen zeitinvarianten Systemen
* [Elementarsignale](GDET3%20Elementarsignale.ipynb) (Elementarsignale sowie Zeitverschiebung, -spiegelung, -dehnung)
* [Demo Zeitverschiebung, -spiegelung, -dehnung](GDET3%20Zeitverschiebung-Dehnung%20GUI.ipynb)
* [Beispiel zur Berechnung des Faltungsintegrals](GDET3%20Faltungsbeispiel.ipynb)
* [Demo Faltungsintegral](GDET3%20Faltung%20GUI.ipynb)
2. Laplace-Transformation
* [Beispiele Laplace-Transformation](GDET3%20Laplace-Transformation.ipynb)
* [Demo Laplace-Transformation](GDET3%20Laplace-Transformation%20GUI.ipynb)
* [RLC System](GDET3%20RLC-System.ipynb)
3. Fourier-Beschreibung von Signalen und Systemen
* [RL-Hochpass](GDET3%20RL-Hochpass.ipynb)
* [Fourier-Reihe](GDET3%20Fourier-Reihenkoeffizienten.ipynb)
* [Demo Fourier-Transformation](GDET3%20Fourier-Transformation.ipynb)
* [Demo Übertragungsfunktion](GDET3%20Übertragungsfunktion.ipynb)
4. Diskrete Signale und Systeme
* [Ideale Abtastung](GDET3%20Ideale%20Abtastung.ipynb)
* [Reale Abtastung](GDET3%20Reale%20Abtastung.ipynb)
* [Demo Diskretes Faltungsintegral](GDET3%20Diskrete%20Faltung%20GUI.ipynb)
* [Diskrete Fourier-Transformation](GDET3%20Diskrete%20Fourier-Transformation.ipynb)
* [Demo Diskrete Fourier-Transformation](GDET3%20Diskrete%20Fourier-Transformation%20GUI.ipynb)
* [Demo $z$-Transformation](GDET3%20z-Transformation%20GUI.ipynb)
5. Systemtheorie der Tiefpass- und Bandpasssysteme
* [Äquivalenter Tiefpass](GDET3%20Äquivalenter%20Tiefpass.ipynb)
6. Korrelationsfunktionen determinierter Signale
* [Demo Auto- und Kreuzkorrelationsfunktion](GDET3%20Autokorrelationsfunktion.ipynb)
* [Demo Korrelationsempfänger](GDET3%20Korrelationsempfänger.ipynb)
7. Statistische Signalbeschreibung
* [Verteilungs- und Verteilungsdichtefunktion](GDET3%20Verteilungsfunktion.ipynb)
* [Demo Verteilungs- und Verteilungsdichtefunktion](GDET3%20Verteilungsfunktion%20GUI.ipynb)
* [Weißes Rauschen](GDET3%20Weißes%20Rauschen.ipynb)
* [Zufallssignale in LTI-Systemen](GDET3%20Zufallssignale%20in%20LTI-Systemen.ipynb)
%% Cell type:markdown id: tags:
Zusätzlich steht ein [Primer](GDET3%20Primer%20Komplexe%20Zahlen.ipynb) zu komplexen Zahlen zur Verfügung.
## Mitwirkende
* [Christian Rohlfing](http://www.ient.rwth-aachen.de/cms/c_rohlfing/)
* [Iris Heisterklaus](http://www.ient.rwth-aachen.de/cms/i_heisterklaus/)
* Emin Kosar
%% 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:
*Christian Rohlfing, Übungsbeispiele zur Vorlesung "Grundgebiete der Elektrotechnik 3 - Signale und Systeme"*, gehalten von Jens-Rainer Ohm, 2020, [Institut für Nachrichtentechnik](http://www.ient.rwth-aachen.de), RWTH Aachen University.
......
......@@ -25,7 +25,7 @@ class pzPoint():
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
if self.h_order is not None: self.h_order[0].remove(); self.h_order[1].remove(); self.h_order = None
class pzPlot():
......@@ -106,26 +106,96 @@ class pzPlot():
self.ax.set_title('Pol- /Nullstellen Diagramm', 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
# 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):
if event.inaxes != self.ax:
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
return
if self.filter != 'man': return
# 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 onkeypress(event):
self.action_locked = True if self.action == 'del' else False
self.w_action_type.children[0].value = 'Löschen'
def on_motion(event):
if event.inaxes != self.ax: return
if self.filter != 'man': return
# 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)
def onkeyrelease(event):
if not self.action_locked:
self.w_action_type.children[0].value = 'Hinzufügen'
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 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('Impulsantwort', fontsize='12')
......@@ -433,7 +503,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
......
......@@ -25,7 +25,7 @@ class pzPoint():
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
if self.h_order is not None: self.h_order[0].remove(); self.h_order[1].remove(); self.h_order = None
class zPlot():
......@@ -103,26 +103,99 @@ class zPlot():
self.ax.set_title('Pol- /Nullstellen Diagramm', 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):
if event.inaxes != self.ax:
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
return
if self.filter != 'man': return
def onkeypress(event):
self.action_locked = self.action == 'del'
self.w_action_type.children[0].value = 'Löschen'
# 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 event.inaxes != self.ax: return
if self.filter != 'man': return
def onkeyrelease(event):
if not self.action_locked:
self.w_action_type.children[0].value = 'Hinzufügen'
# 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 on_click(event):
if self.filter != 'man': return
if event.inaxes != self.ax: return
# 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);
......@@ -442,11 +515,13 @@ 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)
rwth_plots.update_ylim(self.handles['axH'], H_f, 1.9, ymax=1e5)
else:
H_f = np.ones(self.f.shape) * -.5
H_f = np.empty(self.f.shape)
H_f[:] = np.nan
rwth_plots.update_ylim(self.handles['axH'], [-0.5, 0.5], 0.1)
self.stabilitytxt.set_visible(not self.systemIsStable)
self.handles['lineH'].set_ydata(H_f)
rwth_plots.update_ylim(self.handles['axH'], H_f, 1.9, ymax=1e5)