Commit 4ba34c92 authored by Christian Rohlfing's avatar Christian Rohlfing
Browse files

- added play functionality to convolution GUI

parent cc8ecfbc
......@@ -7,13 +7,45 @@
import ipywidgets as widgets
from ipywidgets import interact, interactive, fixed, Layout
from IPython.display import clear_output, display, HTML
from scipy import signal # convolution
from scipy.signal import find_peaks # peak finder
from ient_nb.ient_plots import *
from ient_nb.ient_signals import *
def find_intervals(s, thresh, delta):
"""
Find intervals of signal s by searching for delta-functions in the second derivative of s
Parameters
----------
s : array_like
The signal
thresh : float
Threshold for delta search
delta : float
Sampling period
Returns
-------
intervals_s, peaks, dd : *intervals* are the intervals, *peaks* the found peaks and *dd* the second derivative of s.
"""
# derivative of derivative shows delta functions at discontinuities
dd = np.diff(np.diff(s, prepend=0), prepend=0)/delta**2
# find delta functions
peaks, _ = find_peaks(np.abs(dd), prominence=thresh)
# return interval limits in seconds
intervals = np.round(tau[peaks]*10)/10
return intervals, peaks, dd
```
%% Cell type:markdown id: tags:
<div>
......@@ -52,32 +84,46 @@
``` 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 Achse
(tau,deltat) = np.linspace(-15, 15, 10001, retstep=True) # tau axis
interval_cnt = 0; # Interval counter for stepwise plot
signal_types = {'Rechteck' : rect,
'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,
}
# Signals
signal_types = {
'Rechteck' : rect,
'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):
global s, h, gt # reused in second interactive plot
s = lambda tau: signal_types[s_type]((tau-s_t0)/s_T);
# 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), 0.49*np.max(np.abs(s(tau)))/deltat, deltat)
intervals_h,_,_ = find_intervals(h(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');
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]); ient_axis(ax); ient_grid(ax);
......@@ -90,10 +136,12 @@
try: # if convolution figure is already opened, update s(tau)
if axs[0].lines:
axs[0].lines[1].set_ydata(s(tau));
ient_update_ylim(axs[0], np.concatenate((h(tau), s(tau))), 0.19, 5); ient_update_ylim(axs[1], gt, 0.19, 5);
update_plot(-2) # update convolution plot
update_plot_intervals() # update interval lines
except: pass
ient_update_ylim(axs0[0], s(tau), 0.19, 5); ient_update_ylim(axs0[1], h(tau), 0.19, 5);
# Widgets
w_s_type = widgets.Dropdown(options=list(signal_types.keys()), description=r'Wähle $s(t)$:')
......@@ -112,16 +160,21 @@
%% Cell type:code id: tags:
``` python
fig, axs = plt.subplots(2, 1, figsize=(12, 12/ient_fig_aspect)) # gridspec_kw = {'width_ratios':[3, 1]}
@widgets.interact(t=widgets.FloatSlider(min=-4, max=4, value=-2, step=.1, description='Verschiebung $t$', style=ient_wdgtl_style),
t_w = widgets.FloatSlider(min=-4, max=4, value=-2, step=.1, description='Verschiebung $t$', style=ient_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=ient_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', label=r'$h(t-\tau)$'); # plot h(t-tau)
ax.plot(tau, s(tau), 'grun', label=r'$s(\tau)$'); # plot s(tau)
ax.plot(tau, sh, '--', color='orange', lw=1, label=r'$s(\tau)h(t-\tau)$'); # plot integrand
ient_annotate_xtick(ax, r'$t$', t, -0.1, 'rwth', 15); # mark t on tau axis
......@@ -143,10 +196,76 @@
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_colors['rot'],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:
### Aufgaben:
......
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