Commit dc873e8a authored by Nicolas Ewald Alfons Horst's avatar Nicolas Ewald Alfons Horst
Browse files

initial commit

parent b4e38a0e
Pipeline #340065 passed with stage
in 2 minutes and 40 seconds
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"hide_input": false,
"jupyter": {
"source_hidden": true
}
},
"outputs": [],
"source": [
"# Copyright 2020 Institut für Nachrichtentechnik, RWTH Aachen University\n",
"%matplotlib widget\n",
"import ipywidgets as widgets\n",
"from ipywidgets import interact, interactive\n",
"\n",
"import numpy as np\n",
"import matplotlib.pyplot as plt\n",
"import scipy as sp\n",
"import scipy.special\n",
"import scipy.signal\n",
"\n",
"import rwth_nb.plots.mpl_decorations as rwth_plots\n",
"from rwth_nb.misc.signals import *\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"hide_input": false,
"jupyter": {
"RWTH_LANG": "DE",
"source_hidden": true
},
"lines_to_next_cell": 2
},
"outputs": [],
"source": [
"# language settings\n",
"import gettext\n",
"lng = gettext.translation('messages', localedir='locale', languages=['de'])\n",
"lng.install()\n",
"_ = lng.gettext"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<div>\n",
" <img src=\"figures/rwth_ient_logo@2x.png\" style=\"float: right;height: 5em;\">\n",
"</div>"
]
},
{
"cell_type": "markdown",
"metadata": {
"RWTH_LANG": "DE",
"lines_to_next_cell": 0
},
"source": [
"# Auto- und Kreuzkorrelationsfunktion von Energiesignalen\n",
"Zum Starten: Im Menü: Run <span class=\"fa-chevron-right fa\"></span> Run All Cells auswählen. \n",
"## Einleitung\n",
"\n",
"### Energiesignal\n",
"\n",
"Ein Signal $s(t)$ heißt Energiesignal, wenn seine Signalenergie endlich ist, es gilt also"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"$$E_s = \\int\\limits_{-\\infty}^\\infty |s(t)|^2 \\mathrm{d} t < \\infty \\text{ .}$$ "
]
},
{
"cell_type": "markdown",
"metadata": {
"RWTH_LANG": "DE",
"lines_to_next_cell": 0
},
"source": [
"Viele wichtige Signale haben keine endliche Gesamtenergie, z. B. alle periodischen Signale, die Sprungfunktion oder zeitlich nicht begrenzte Zufallssignale. Für Signale mit Dirac-Impulsen ist die Energie nicht definiert.\n",
"\n",
"### Kreuzkorrelationsfunktion\n",
"Für zwei Energiesignale $s(t)$ und $g(t)$ kann die *Kreuzkorrelationsfunktion* berechnet werden. Diese zeigt an, wie ähnlich sich zwei Signale bei unterschiedlichen Verschiebungen $\\tau$ sind."
]
},
{
"cell_type": "markdown",
"metadata": {
"lines_to_next_cell": 0
},
"source": [
"$$\n",
"\\varphi_{sg}^\\mathrm{E}(\\tau) \n",
"= \\int\\limits_{-\\infty}^\\infty s^\\ast(t) g(t+\\tau)\\mathrm{d}t \n",
"\\stackrel{t\\rightarrow -t}{=} \\int\\limits_{-\\infty}^\\infty s(-t)^\\ast g(\\tau-t) \\mathrm{d} t \n",
"= s^\\ast(-\\tau) \\ast g(\\tau)\n",
"= \\left[\\varphi_{gs}^\\mathrm{E}(-\\tau)\\right]^\\ast\n",
"$$"
]
},
{
"cell_type": "markdown",
"metadata": {
"RWTH_LANG": "DE",
"lines_to_next_cell": 0
},
"source": [
"Die Kreuzkorrelationsfunktionen $\\varphi_{sg}^\\mathrm{E}(\\tau)$ und $\\varphi_{gs}^\\mathrm{E}(\\tau)$ sind also zueinander zeitgespiegelt und konjugiert-komplex.\n",
"Für die Berechnung der Kreuzkorrelationsfunktion müssen die beiden Signale nicht zwingend Energiesignale sein, es ist ausreichend, dass das Integral berechnet werden kann. Man beachte hier auch die Ähnlichkeit zur Faltung zweier Signale, bei der Berechnung kann entsprechend vorgegangen werden. \n",
"### Autokorrelationsfunktion\n",
"\n",
"Die Autokorrelationsfunktion entspricht der Kreuzkorrelationsfunktion, wenn $g(t) = s(t)$ gilt. Sie zeigt an, wie ähnlich ein Signal sich selber bei einer zeitlichen Verschiebung $\\tau$ ist"
]
},
{
"cell_type": "markdown",
"metadata": {
"lines_to_next_cell": 0
},
"source": [
"$$\n",
"\\varphi_{ss}^\\mathrm{E}(\\tau) \n",
"= \\int\\limits_{-\\infty}^\\infty s^\\ast(t) s(t+\\tau)\\mathrm{d}t \\text{.}\n",
"$$"
]
},
{
"cell_type": "markdown",
"metadata": {
"RWTH_LANG": "DE",
"lines_to_next_cell": 0
},
"source": [
"Die Autokorrelationsfunktion besitzt folgende allgemeingültige Eigenschaften:\n",
"* Die Autokorrelationsfunktion ist immer eine gerade Funktion.\n",
"* Den maximalen Wert nimmt eine Autokorrelationsfunktion für $\\tau=0$ an, da in diesem Fall größte Ähnlichkeit vorliegt. \n",
"* Das Maximum der Autokorrelationsfunktion eines Energiesignals ist gleich seiner Energie"
]
},
{
"cell_type": "markdown",
"metadata": {
"lines_to_next_cell": 0
},
"source": [
"$$\\varphi_{ss}^\\mathrm{E}(0)=E_s \\text{.}$$"
]
},
{
"cell_type": "markdown",
"metadata": {
"RWTH_LANG": "DE",
"lines_to_next_cell": 0
},
"source": [
"* Bei zeitlich begrenzten Signalen hat die Autokorrelationsfunktion die doppelte Breite des Signals.\n",
"\n",
"\n",
"## Interaktive Demo\n",
"Die interaktive Demo ermöglicht es, sich die Kreuz- und Autokorrelation für verschiedene Signale anzusehen. "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"$$\n",
"\\varphi_{sg}^\\mathrm{E}(\\tau) \n",
"= s^\\ast(-\\tau) \\ast g(\\tau) \n",
"= g(\\tau) \\ast s^\\ast(-\\tau)\n",
"= \\int\\limits_{-\\infty}^\\infty g(t) s^\\ast(t-\\tau) \\mathrm{d} t \n",
"$$"
]
},
{
"cell_type": "markdown",
"metadata": {
"RWTH_LANG": "DE"
},
"source": [
"Es ist auch möglich, eine eigene Funktion $s_0(t)$ zu definieren. "
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"s_0 = lambda t: rect(t/2-1/2)*(-t)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"jupyter": {
"source_hidden": true
}
},
"outputs": [],
"source": [
"def correlation(s, g):\n",
" # Correlate s and g numerically\n",
" return sp.signal.convolve(np.conj(s(-t)),g(t), mode='full')*deltat\n",
"\n",
"fs = 2000 # Samplingrate\n",
"(t,deltat) = np.linspace(-8,8, 16*fs, retstep=True) # Zeitachse\n",
"(tau,deltatau) = np.linspace(-16,16, 2*len(t)-1, retstep=True) # Korrelation\n",
"\n",
"signal_types = {_('rectangle' ): rect,\n",
" _('triangle' ): tri, \n",
" _('step function' ): unitstep, \n",
" _('si-function' ): lambda t: si(t*np.pi), \n",
" _('exponential pulse' ): lambda t: unitstep(t)*np.exp(-t),\n",
" _('Gauss signal' ): gauss, \n",
" _('double rectangle' ): lambda t: rect(t*2+0.5)-rect(t*2-0.5),\n",
" _('ramp' ): lambda t: t*rect(t-0.5), \n",
" _('shifted rectangle' ): lambda t: -rect(t-0.5),\n",
" _('own creation' ): s_0,\n",
" }"
]
},
{
"cell_type": "markdown",
"metadata": {
"RWTH_LANG": "DE"
},
"source": [
"Wähle Signale für $s(t)$ und $g(t)$ im Drop-Down-Menü aus. Für beide Signale kann auch die Breite $T$ und die Verschiebung $t_0$ angepasst werden. Die jeweiligen Signale werden dann in der nachfolgenden Abbildung angezeigt."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"hide_input": false,
"jupyter": {
"source_hidden": true
}
},
"outputs": [],
"source": [
"fig0, axs0 = plt.subplots(1, 2, figsize=(8,2))\n",
"@widgets.interact(s_type=widgets.Dropdown(options=list(signal_types.keys()), description=r'Wähle $s(t)$:'),\n",
" s_T=widgets.FloatSlider(min=0.5, max=4, value=1, step=.1, description=r'Dehnung T', style=rwth_plots.wdgtl_style), \n",
" s_t0=widgets.FloatSlider(min=-2, max=2, value=0, step=.1, description=r'Verschiebung $t_0$', style=rwth_plots.wdgtl_style), \n",
" g_type=widgets.Dropdown(options=list(signal_types.keys()), description=r'Wähle $g(t)$:'),\n",
" g_T=widgets.FloatSlider(min=0.5, max=4, value=1, step=.1, description=r'Dehnung T', style=rwth_plots.wdgtl_style), \n",
" g_t0=widgets.FloatSlider(min=-2, max=2, value=0, step=.1, description=r'Verschiebung $t_0$', style=rwth_plots.wdgtl_style))\n",
"def update_signals(s_type, s_T, s_t0, g_type, g_T, g_t0):\n",
" global s, g, phi_sg # reused in second interactive plot\n",
" s = lambda t: signal_types[s_type]((t-s_t0)/s_T);\n",
" g = lambda t: signal_types[g_type]((t-g_t0)/g_T);\n",
" phi_sg = correlation(s, g) # numerical correlation\n",
" \n",
" if not axs0[0].lines: # plot s(t) and g(t)\n",
" ax = axs0[0]; ax.plot(t, s(t), 'rwth:blue');\n",
" ax.set_xlabel(r'$\\rightarrow t$'); ax.set_ylabel(r'$\\uparrow s(t)$')\n",
" ax.set_xlim([-2.9, 2.9]); ax.set_ylim([-1.19, 1.19]); rwth_plots.axis(ax); rwth_plots.grid(ax);\n",
" \n",
" ax = axs0[1]; ax.plot(t, g(t), 'rwth:blue');\n",
" ax.set_xlabel(r'$\\rightarrow t$'); ax.set_ylabel(r'$\\uparrow g(t)$')\n",
" ax.set_xlim(axs0[0].get_xlim()); ax.set_ylim(axs0[0].get_ylim()); rwth_plots.axis(ax); rwth_plots.grid(ax);\n",
" else: # update lines\n",
" axs0[0].lines[0].set_ydata(s(t)); \n",
" axs0[1].lines[0].set_ydata(g(t));\n",
" try: # if convolution figure is already opened, update s(t)\n",
" if axs[0].lines:\n",
" axs[0].lines[0].set_ydata((g(t)));\n",
" rwth_plots.update_ylim(axs[0], np.concatenate((g(t), s(t))), 0.19, 5); rwth_plots.update_ylim(axs[1], phi_sg, 0.19, 5);\n",
" update_plot(-2); # update correlation plot\n",
" except: pass\n",
" rwth_plots.update_ylim(axs0[0], s(t), 0.19, 5); rwth_plots.update_ylim(axs0[1], g(t), 0.19, 5); "
]
},
{
"cell_type": "markdown",
"metadata": {
"RWTH_LANG": "DE"
},
"source": [
"In der nachfolgenden Grafik kann für die beiden oben ausgewählten Funktionen nun das Ergebnis der Kreuzkorrelationsfunktion betrachtet werden. Hierfür muss der Schieberegler von links nach rechts geschoben werden. Wenn das Kästchen bei Integrand angeklickt ist, wird die aktuell überlappende Fläche der beiden Funktionen gestrichelt angezeigt. Diese entspricht dem Wert der Kreuzkorrelationsfunktion. Dies kann im unteren Teil der Grafik interaktiv betrachtet werden. "
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"hide_input": false,
"jupyter": {
"source_hidden": true
}
},
"outputs": [],
"source": [
"fig, axs=plt.subplots(2, 1, figsize=(8, 8 / rwth_plots.fig_aspect))\n",
"@widgets.interact(tau_shft=widgets.FloatSlider(min=-4, max=4, value=-2, step=.1, description=r'Verschiebung $\\tau$', style=rwth_plots.wdgtl_style), \n",
" show_integrand=widgets.Checkbox(value=True, description='Zeige Integrand', style=rwth_plots.wdgtl_style))\n",
"def update_plot(tau_shft, show_integrand=True):\n",
" tau_ind = np.where(tau>=tau_shft); tau_ind = tau_ind[0][0]; phi_plot = phi_sg.copy(); phi_plot[tau_ind:] = np.nan; # hide g(t') with t'>t\n",
" sg = np.conj(s(t-tau_shft))*g(t) # integrand\n",
" \n",
" if not axs[0].lines: # Call plot() and decorate axes. Usually, these functions take some processing time\n",
" ax = axs[0]; ax.plot(t, g(t), 'rwth:blue', label=r'$g(t)$'); # plot g(t)\n",
" ax.plot(t, np.conj(s(t-tau_shft)), 'rwth:green', label=r'$s^\\ast(t-\\tau)$'); # plot s(t-tau)\n",
" ax.plot(t, sg, '--', color='rwth:orange', lw=1, label=r'$g(t)s^\\ast(t-\\tau)$'); # plot integrand\n",
" rwth_plots.annotate_xtick(ax, r'$\\tau$', tau_shft, -0.1, 'rwth:blue', 15); # mark t on tau axis\n",
" ax.fill_between(t, 0, sg, facecolor=\"none\", hatch=\"//\", edgecolor='rwth:black', linewidth=0.0); # hatch common area\n",
" ax.set_xlabel(r'$\\rightarrow t$');\n",
" ax.set_xlim([-4.2,4.2]); rwth_plots.update_ylim(ax, np.concatenate((g(t), s(t))), 0.19, 5);\n",
" ax.legend(); rwth_plots.axis(ax); rwth_plots.grid(ax); \n",
" \n",
" ax = axs[1]; ax.plot(tau, phi_plot); # plot phi_sg(tau)\n",
" ax.plot([tau_shft, tau_shft], [0, phi_sg[tau_ind]], 'ko--', lw=1);\n",
" ax.set_xlabel(r'$\\rightarrow \\tau$'); \n",
" ax.set_ylabel(r'$\\uparrow \\varphi_{sg}^\\mathrm{E}(\\tau) = \\int g(t) s^\\ast(t-\\tau)\\mathrm{d}t$'); \n",
" ax.set_xlim(axs[0].get_xlim()); rwth_plots.update_ylim(ax, phi_sg, 0.19, 5); \n",
" rwth_plots.axis(ax); rwth_plots.grid(ax); fig.tight_layout(); \n",
" \n",
" else: # Replace only xdata and ydata since plt.plot() takes longer time\n",
" ax = axs[0]; ax.lines[1].set_ydata(np.conj(s(t-tau_shft))); ax.lines[2].set_ydata(sg); # update signals\n",
" ax.texts[0].set_x(tau_shft); ax.lines[3].set_xdata([tau_shft,tau_shft]) # update labels\n",
" if ax.collections: ax.collections[0].remove(); # update integrand\n",
" if show_integrand: ax.fill_between(t,0,sg, facecolor=\"none\", hatch=\"//\", edgecolor='rwth:black', linewidth=0.0);\n",
" ax = axs[1]; ax.lines[0].set_ydata(phi_plot); # update signals\n",
" ax.lines[1].set_xdata([tau_shft, tau_shft]); ax.lines[1].set_ydata([0, phi_sg[tau_ind]]); # update labels\n",
"\n",
" axs[0].lines[2].set_visible(show_integrand)"
]
},
{
"cell_type": "markdown",
"metadata": {
"RWTH_LANG": "DE",
"lines_to_next_cell": 2
},
"source": [
"## Aufgaben\n",
"* Wähle zwei gleiche Funktionen aus. Überprüfe die oben angegebenen Eigenschaften der Autokorrelationsfunktion. Variiere hierbei die Funktionen und betrachte die Autokorrelationsfunktion verschiedener Signale. \n",
"* Wie ändert sich das Ergebnis, wenn die Breite eines der beiden Signale sich ändert?\n",
"* Wähle zwei verschiedene Signale aus und beobachte, wo in diesen Fällen die maximale Kreuzkorrelation auftritt. Kannst du erklären, warum?"
]
},
{
"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, Übungsbeispiele zur Vorlesung \"Grundgebiete der Elektrotechnik 3 - Signale und Systeme\"*, gehalten von Jens-Rainer Ohm, 2020, Institut für Nachrichtentechnik, RWTH Aachen University."
]
}
],
"metadata": {
"jupytext": {
"formats": "md,ipynb"
},
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
}
},
"nbformat": 4,
"nbformat_minor": 4
}
---
jupyter:
jupytext:
formats: ipynb,md
text_representation:
extension: .md
format_name: markdown
format_version: '1.2'
jupytext_version: 1.6.0
kernelspec:
display_name: Python 3
language: python
name: python3
---
```python hide_input=false jupyter={"source_hidden": true}
# Copyright 2020 Institut für Nachrichtentechnik, RWTH Aachen University
%matplotlib widget
import ipywidgets as widgets
from ipywidgets import interact, interactive
import numpy as np
import matplotlib.pyplot as plt
import scipy as sp
import scipy.special
import scipy.signal
import rwth_nb.plots.mpl_decorations as rwth_plots
from rwth_nb.misc.signals import *
```
```python hide_input=false jupyter={"source_hidden": true, "RWTH_LANG": "DE"}
# language settings
import gettext
lng = gettext.translation('messages', localedir='locale', languages=['de'])
lng.install()
_ = lng.gettext
```
```python hide_input=false jupyter={"source_hidden": true, "RWTH_LANG": "EN"}
# default for english language
_ = lambda s: s
```
<div>
<img src="figures/rwth_ient_logo@2x.png" style="float: right;height: 5em;">
</div>
<!-- #region RWTH_LANG="DE" -->
# Auto- und Kreuzkorrelationsfunktion von Energiesignalen
Zum Starten: Im Menü: Run <span class="fa-chevron-right fa"></span> Run All Cells auswählen.
## Einleitung
### Energiesignal
Ein Signal $s(t)$ heißt Energiesignal, wenn seine Signalenergie endlich ist, es gilt also
<!-- #endregion -->
<!-- #region RWTH_LANG="EN" -->
# Auto and cross correlation function of energy signals
To start: In the menu: Run <span class="fa-chevron-right fa"></span> Select Run All Cells.
## Introduction
### Energy signal
A signal $s(t)$ is called energy signal when its signal energy is finite, so
<!-- #endregion -->
$$E_s = \int\limits_{-\infty}^\infty |s(t)|^2 \mathrm{d} t < \infty \text{ .}$$
<!-- #region RWTH_LANG="DE" -->
Viele wichtige Signale haben keine endliche Gesamtenergie, z. B. alle periodischen Signale, die Sprungfunktion oder zeitlich nicht begrenzte Zufallssignale. Für Signale mit Dirac-Impulsen ist die Energie nicht definiert.
### Kreuzkorrelationsfunktion
Für zwei Energiesignale $s(t)$ und $g(t)$ kann die *Kreuzkorrelationsfunktion* berechnet werden. Diese zeigt an, wie ähnlich sich zwei Signale bei unterschiedlichen Verschiebungen $\tau$ sind.
<!-- #endregion -->
<!-- #region RWTH_LANG="EN" -->
Many important signals do not have a finite total energy, e.g. all periodic signals, the step function or random signals that are not limited in time. For signals with Dirac pulses the energy is not defined.
### Cross Correlation Function
For two energy signals $s(t)$ and $g(t)$, the *cross correlation function* can be calculated. This shows how similar two signals are at different displacements $\tau$.
<!-- #endregion -->
$$
\varphi_{sg}^\mathrm{E}(\tau)
= \int\limits_{-\infty}^\infty s^\ast(t) g(t+\tau)\mathrm{d}t
\stackrel{t\rightarrow -t}{=} \int\limits_{-\infty}^\infty s(-t)^\ast g(\tau-t) \mathrm{d} t
= s^\ast(-\tau) \ast g(\tau)
= \left[\varphi_{gs}^\mathrm{E}(-\tau)\right]^\ast
$$
<!-- #region RWTH_LANG="DE" -->
Die Kreuzkorrelationsfunktionen $\varphi_{sg}^\mathrm{E}(\tau)$ und $\varphi_{gs}^\mathrm{E}(\tau)$ sind also zueinander zeitgespiegelt und konjugiert-komplex.
Für die Berechnung der Kreuzkorrelationsfunktion müssen die beiden Signale nicht zwingend Energiesignale sein, es ist ausreichend, dass das Integral berechnet werden kann. Man beachte hier auch die Ähnlichkeit zur Faltung zweier Signale, bei der Berechnung kann entsprechend vorgegangen werden.
### Autokorrelationsfunktion
Die Autokorrelationsfunktion entspricht der Kreuzkorrelationsfunktion, wenn $g(t) = s(t)$ gilt. Sie zeigt an, wie ähnlich ein Signal sich selber bei einer zeitlichen Verschiebung $\tau$ ist
<!-- #endregion -->
<!-- #region RWTH_LANG="EN" -->
The cross-correlation functions $\varphi_{sg}^\mathrm{E}(\tau)$ and $\varphi_{gs}^\mathrm{E}(\tau)$ are therefore time-mirrored and complex-conjugated to each other.
For the calculation of the cross correlation function, the two signals do not necessarily have to be energy signals, it is sufficient that the integral can be calculated. Note here also the similarity to the convolution of two signals, the calculation can be done accordingly.
### Autocorrelation function
The autocorrelation function corresponds to the cross-correlation function when $g(t) = s(t)$ applies. It shows how similar a signal is to itself in case of a time shift $\tau$.
<!-- #endregion -->
$$
\varphi_{ss}^\mathrm{E}(\tau)
= \int\limits_{-\infty}^\infty s^\ast(t) s(t+\tau)\mathrm{d}t \text{.}
$$
<!-- #region RWTH_LANG="DE" -->
Die Autokorrelationsfunktion besitzt folgende allgemeingültige Eigenschaften:
* Die Autokorrelationsfunktion ist immer eine gerade Funktion.
* Den maximalen Wert nimmt eine Autokorrelationsfunktion für $\tau=0$ an, da in diesem Fall größte Ähnlichkeit vorliegt.
* Das Maximum der Autokorrelationsfunktion eines Energiesignals ist gleich seiner Energie
<!-- #endregion -->
<!-- #region RWTH_LANG="EN" -->
The autocorrelation function has the following general properties:
* The autocorrelation function is always an even function.
* An autocorrelation function takes the maximum value for $\tau=0$, because in this case there is the greatest similarity.
* The maximum of the autocorrelation function of an energy signal is equal to its energy
<!-- #endregion -->
$$\varphi_{ss}^\mathrm{E}(0)=E_s \text{.}$$
<!-- #region RWTH_LANG="DE" -->
* Bei zeitlich begrenzten Signalen hat die Autokorrelationsfunktion die doppelte Breite des Signals.
## Interaktive Demo
Die interaktive Demo ermöglicht es, sich die Kreuz- und Autokorrelation für verschiedene Signale anzusehen.
<!-- #endregion -->
<!-- #region RWTH_LANG="EN" -->
* In the case of time-limited signals, the autocorrelation function has twice the width of the signal.
## Interactive Demo
The interactive demo allows you to view cross and auto-correlation for different signals.
<!-- #endregion -->
$$
\varphi_{sg}^\mathrm{E}(\tau)
= s^\ast(-\tau) \ast g(\tau)
= g(\tau) \ast s^\ast(-\tau)
= \int\limits_{-\infty}^\infty g(t) s^\ast(t-\tau) \mathrm{d} t
$$
<!-- #region RWTH_LANG="DE" -->
Es ist auch möglich, eine eigene Funktion $s_0(t)$ zu definieren.
<!-- #endregion -->
<!-- #region RWTH_LANG="EN" -->
It is also possible to define a custom function $s_0(t)$.
<!-- #endregion -->
```python
s_0 = lambda t: rect(t/2-1/2)*(-t)
```
```python jupyter={"source_hidden": true}
def correlation(s, g):
# Correlate s and g numerically
return sp.signal.convolve(np.conj(s(-t)),g(t), mode='full')*deltat
fs = 2000 # Samplingrate
(t,deltat) = np.linspace(-8,8, 16*fs, retstep=True) # Zeitachse
(tau,deltatau) = np.linspace(-16,16, 2*len(t)-1, retstep=True) # Korrelation
signal_types = {_('rectangle' ): rect,
_('triangle' ): tri,
_('step function' ): unitstep,
_('si-function' ): lambda t: si(t*np.pi),
_('exponential pulse' ): lambda t: unitstep(t)*np.exp(-t),
_('Gauss signal' ): gauss,
_('double rectangle' ): lambda t: rect(t*2+0.5)-rect(t*2-0.5),
_('ramp' ): lambda t: t*rect(t-0.5),
_('shifted rectangle' ): lambda t: -rect(t-0.5),
_('own creation' ): s_0,
}
```
<!-- #region RWTH_LANG="DE" -->
Wähle Signale für $s(t)$ und $g(t)$ im Drop-Down-Menü aus. Für beide Signale kann auch die Breite $T$ und die Verschiebung $t_0$ angepasst werden. Die jeweiligen Signale werden dann in der nachfolgenden Abbildung angezeigt.
<!-- #endregion -->
<!-- #region RWTH_LANG="EN" -->
Select signals for $s(t)$ and $g(t)$ from the drop-down menu. For both signals the width $T$ and the offset $t_0$ can also be adjusted. The respective signals are then displayed in the figure below.
<!-- #endregion -->
```python hide_input=false jupyter={"source_hidden": true}
fig0, axs0 = plt.subplots(1, 2, figsize=(8,2))
@widgets.interact(s_type=widgets.Dropdown(options=list(signal_types.keys()), description=r'Wähle $s(t)$:'),
s_T=widgets.FloatSlider(min=0.5, max=4, value=1, step=.1, description=r'Dehnung T', style=rwth_plots.wdgtl_style),
s_t0=widgets.FloatSlider(min=-2, max=2, value=0, step=.1, description=r'Verschiebung $t_0$', style=rwth_plots.wdgtl_style),
g_type=widgets.Dropdown(options=list(signal_types.keys()), description=r'Wähle $g(t)$:'),
g_T=widgets.FloatSlider(min=0.5, max=4, value=1, step=.1, description=r'Dehnung T', style=rwth_plots.wdgtl_style),
g_t0=widgets.FloatSlider(min=-2, max=2, value=0, step=.1, description=r'Verschiebung $t_0$', style=rwth_plots.wdgtl_style))
def update_signals(s_type, s_T, s_t0, g_type, g_T, g_t0):
global s, g, phi_sg # reused in second interactive plot
s = lambda t: signal_types[s_type]((t-s_t0)/s_T);
g = lambda t: signal_types[g_type]((t-g_t0)/g_T);
phi_sg = correlation(s, g) # numerical correlation
if not axs0[0].lines: # plot s(t) and g(t)
ax = axs0[0]; ax.plot(t, s(t), '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(t, g(t), 'rwth:blue');
ax.set_xlabel(r'$\rightarrow t$'); ax.set_ylabel(r'$\uparrow g(t)$')
ax.set_xlim(axs0[0].get_xlim()); ax.set_ylim(axs0[0].get_ylim()); rwth_plots.axis(ax); rwth_plots.grid(ax);
else: # update lines
axs0[0].lines[0].set_ydata(s(t));
axs0[1].lines[0].set_ydata(g(t));
try: # if convolution figure is already opened, update s(t)
if axs[0].lines:
axs[0].lines[0].set_ydata((g(t)));
rwth_plots.update_ylim(axs[0], np.concatenate((g(t), s(t))), 0.19, 5); rwth_plots.update_ylim(axs[1], phi_sg, 0.19, 5);
update_plot(-2); # update correlation plot
except: pass
rwth_plots.update_ylim(axs0[0], s(t), 0.19, 5); rwth_plots.update_ylim(axs0[1], g(t), 0.19, 5);
```
<!-- #region RWTH_LANG="DE" -->
In der nachfolgenden Grafik kann für die beiden oben ausgewählten Funktionen nun das Ergebnis der Kreuzkorrelationsfunktion betrachtet werden. Hierfür muss der Schieberegler von links nach rechts geschoben werden. Wenn das Kästchen bei Integrand angeklickt ist, wird die aktuell überlappende Fläche der beiden Funktionen gestrichelt angezeigt. Diese entspricht dem Wert der Kreuzkorrelationsfunktion. Dies kann im unteren Teil der Grafik interaktiv betrachtet werden.
<!-- #endregion -->
<!-- #region RWTH_LANG="EN" -->
In the following diagram, the result of the cross-correlation function can now be viewed for the two functions selected above. For this purpose, the slider must be moved from left to right. If the box at integrand is ticked, the currently overlapping area of the two functions is shown dashed. This corresponds to the value of the cross correlation function. This can be viewed interactively in the lower part of the graph.
<!-- #endregion -->
```python hide_input=false jupyter={"source_hidden": true}
fig, axs=plt.subplots(2, 1, figsize=(8, 8 / rwth_plots.fig_aspect))
@widgets.interact(tau_shft=widgets.FloatSlider(min=-4, max=4, value=-2, step=.1, description=r'Verschiebung $\tau$', style=rwth_plots.wdgtl_style),
show_integrand=widgets.Checkbox(value=True, description='Zeige Integrand', style=rwth_plots.wdgtl_style))
def update_plot(tau_shft, show_integrand=True):
tau_ind = np.where(tau>=tau_shft); tau_ind = tau_ind[0][0]; phi_plot = phi_sg.copy(); phi_plot[tau_ind:] = np.nan; # hide g(t') with t'>t
sg = np.conj(s(t-tau_shft))*g(t) # integrand
if not axs[0].lines: # Call plot() and decorate axes. Usually, these functions take some processing time
ax = axs[0]; ax.plot(t, g(t), 'rwth:blue', label=r'$g(t)$'); # plot g(t)
ax.plot(t, np.conj(s(t-tau_shft)), 'rwth:green', label=r'$s^\ast(t-\tau)$'); # plot s(t-tau)
ax.plot(t, sg, '--', color='rwth:orange', lw=1, label=r'$g(t)s^\ast(t-\tau)$'); # plot integrand
rwth_plots.annotate_xtick(ax, r'$\tau$', tau_shft, -0.1, 'rwth:blue', 15); # mark t on tau axis
ax.fill_between(t, 0, sg, facecolor="none", hatch="//", edgecolor='rwth:black', linewidth=0.0); # hatch common area
ax.set_xlabel(r'$\rightarrow t$');
ax.set_xlim([-4.2,4.2]); rwth_plots.update_ylim(ax, np.concatenate((g(t), s(t))), 0.19, 5);
ax.legend(); rwth_plots.axis(ax); rwth_plots.grid(ax);
ax = axs[1]; ax.plot(tau, phi_plot); # plot phi_sg(tau)
ax.plot([tau_shft, tau_shft], [0, phi_sg[tau_ind]], 'ko--', lw=1);
ax.set_xlabel(r'$\rightarrow \tau$');
ax.set_ylabel(r'$\uparrow \varphi_{sg}^\mathrm{E}(\tau) = \int g(t) s^\ast(t-\tau)\mathrm{d}t$');
ax.set_xlim(axs[0].get_xlim()); rwth_plots.update_ylim(ax, phi_sg, 0.19, 5);
rwth_plots.axis(ax); rwth_plots.grid(ax); fig.tight_layout();
else: # Replace only xdata and ydata since plt.plot() takes longer time
ax = axs[0]; ax.lines[1].set_ydata(np.conj(s(t-tau_shft))); ax.lines[2].set_ydata(sg); # update signals