Explorar o código

revision of signal analysis notebook

Gert-Ludwig Ingold %!s(int64=5) %!d(string=hai) anos
pai
achega
6be094cedf
Modificáronse 1 ficheiros con 73 adicións e 70 borrados
  1. 73 70
      notebooks/2_tgv_signal.ipynb

+ 73 - 70
notebooks/2_tgv_signal.ipynb

@@ -11,7 +11,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 1,
+   "execution_count": null,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -27,16 +27,7 @@
    "cell_type": "markdown",
    "metadata": {},
    "source": [
-    "## Rocking motion of a TGV"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "metadata": {},
-   "source": [
-    "Our data are stored in a compressed file `TGV_data.csv.bz2`. Each row of the uncompressed file contains entries separated by commas and the first row contains labels explaining the content of the respective column.\n",
-    "\n",
-    "NumPy provides rather universal tools to import data from files into NumPy arrays. We will use `genfromtxt` which allows to deal with `bz2` compressed data files and also handles the labels in the first row of the data."
+    "### Importing the data"
    ]
   },
   {
@@ -45,23 +36,24 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "np.info(np.genfromtxt)"
+    "data = np.genfromtxt('data/TGV_data.csv.bz2', delimiter=',', names=True)\n",
+    "time = data['Time_s']\n",
+    "omega_y = data['Gyroscope_y_rads']\n",
+    "_ = plt.plot(time, omega_y)"
    ]
   },
   {
-   "cell_type": "code",
-   "execution_count": null,
+   "cell_type": "markdown",
    "metadata": {},
-   "outputs": [],
    "source": [
-    "data = np.genfromtxt('data/TGV_data.csv.bz2', delimiter=',', names=True)"
+    "### Time spacing"
    ]
   },
   {
    "cell_type": "markdown",
    "metadata": {},
    "source": [
-    "There are five columns identified by names:"
+    "Before analyzing the data any further, let us take a look at the time elapsed between subsequent measurements."
    ]
   },
   {
@@ -70,7 +62,7 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "data.dtype.names"
+    "time_intervals = time[1:]-time[:-1]"
    ]
   },
   {
@@ -79,16 +71,14 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "time = data['Time_s']\n",
-    "omega_x = data['Gyroscope_x_rads']\n",
-    "omega_y = data['Gyroscope_y_rads']"
+    "_ = plt.hist(time_intervals, bins=100)"
    ]
   },
   {
    "cell_type": "markdown",
    "metadata": {},
    "source": [
-    "Let us first get an idea of the data."
+    "We will ignore these small differences and assume the data to be equally spaced in time with the mean time difference between subsequent data points."
    ]
   },
   {
@@ -97,22 +87,25 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "plt.plot(time, omega_x)\n",
-    "plt.plot(time, omega_y)"
+    "delta_t = np.mean(time_intervals)\n",
+    "delta_t"
    ]
   },
   {
    "cell_type": "markdown",
    "metadata": {},
    "source": [
-    "### Spectral properties"
+    "### Spectral analysis"
    ]
   },
   {
    "cell_type": "markdown",
    "metadata": {},
    "source": [
-    "Before analyzing the data any further, let us take a look at the time elapsed between subsequent measurements."
+    "Fourier transformation of a discrete real signal with `rfft`:\n",
+    "$$y_j = \\sum_{k=0}^{n-1} x_k \\exp\\left(-\\text{i}\\frac{2\\pi jk}{n}\\right)$$\n",
+    "With exception of $y_0$ and, for even $n$, $y_{n/2}$ all $y_j$ are complex.\n",
+    "`rfft` returns an array $y_0, \\text{Re}(y_1), \\text{Im}(y_1),\\ldots$"
    ]
   },
   {
@@ -121,7 +114,7 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "time_intervals = time[1:]-time[:-1]"
+    "fft_y = fftpack.rfft(omega_y)"
    ]
   },
   {
@@ -130,14 +123,7 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "_ = plt.hist(time_intervals, bins=100)"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "metadata": {},
-   "source": [
-    "We will ignore these small differences and assume the data to be equally spaced in time with the mean time difference between subsequent data points."
+    "_ = plt.plot(fft_y[::2])"
    ]
   },
   {
@@ -146,15 +132,16 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "delta_t = np.mean(time_intervals)\n",
-    "delta_t"
+    "_ = plt.plot(fft_y[1::2])"
    ]
   },
   {
-   "cell_type": "markdown",
+   "cell_type": "code",
+   "execution_count": null,
    "metadata": {},
+   "outputs": [],
    "source": [
-    "We will do the spectral analysis with the data for $\\omega_y$."
+    "freqs = fftpack.rfftfreq(omega_y.shape[0], delta_t)"
    ]
   },
   {
@@ -163,7 +150,7 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "plt.plot(*signal.periodogram(omega_y, 1/delta_t))"
+    "_ = plt.plot(freqs[::2], fft_y[::2])"
    ]
   },
   {
@@ -172,25 +159,23 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "z = fftpack.rfft(omega_y)"
+    "_ = plt.plot(freqs[1::2], fft_y[1::2])"
    ]
   },
   {
-   "cell_type": "code",
-   "execution_count": null,
+   "cell_type": "markdown",
    "metadata": {},
-   "outputs": [],
    "source": [
-    "plt.plot(z[::2])"
+    "<div style=\"color: DarkBlue;background-color: LightYellow\">\n",
+    "<i>Exercise:</i> Given the Fourier transform, how does one get back to the original data?\n",
+    "</div>"
    ]
   },
   {
-   "cell_type": "code",
-   "execution_count": null,
+   "cell_type": "markdown",
    "metadata": {},
-   "outputs": [],
    "source": [
-    "plt.plot(z[1::2])"
+    "### Power spectral density"
    ]
   },
   {
@@ -199,7 +184,11 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "freqs = fftpack.rfftfreq(omega_y.shape[0], delta_t)"
+    "fft_y = np.insert(fft_y, 0, 0)\n",
+    "fft_square = fft_y*fft_y\n",
+    "power = fft_square[::2]+fft_square[1::2]\n",
+    "power = 2*delta_t/fft_square.shape[0]*power\n",
+    "_ = plt.plot(freqs[::2], power)"
    ]
   },
   {
@@ -208,23 +197,21 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "plt.plot(freqs[::2], z[::2])"
+    "_ = plt.plot(*signal.periodogram(omega_y, 1/delta_t))"
    ]
   },
   {
-   "cell_type": "code",
-   "execution_count": null,
+   "cell_type": "markdown",
    "metadata": {},
-   "outputs": [],
    "source": [
-    "plt.plot(freqs[1::2], z[1::2])"
+    "### Filter out the rocking signal"
    ]
   },
   {
    "cell_type": "markdown",
    "metadata": {},
    "source": [
-    "We are interested in the signal around 1.4 Hz. Filter out frequencies beyond 2 Hz."
+    "We are interested in the signal around 1.4 Hz. Filter out frequencies beyond 3 Hz."
    ]
   },
   {
@@ -242,7 +229,7 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "freqs, response = signal.freqz(filter_coeffs, fs=1/delta_t)"
+    "_ = plt.plot(filter_coeffs, '.')"
    ]
   },
   {
@@ -251,7 +238,7 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "plt.plot(freqs, 20*np.log10(np.abs(response)))"
+    "freqs, response = signal.freqz(filter_coeffs, fs=1/delta_t)"
    ]
   },
   {
@@ -260,7 +247,7 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "plt.polar(np.angle(response), np.abs(response))"
+    "_ = plt.plot(freqs, 20*np.log10(np.abs(response)))"
    ]
   },
   {
@@ -269,7 +256,7 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "plt.plot(time, omega_y)"
+    "_ = plt.polar(np.angle(response), np.abs(response))"
    ]
   },
   {
@@ -296,7 +283,7 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "plt.plot(time[150:-150], omega_y[150:-150])"
+    "_ = plt.plot(time[150:-150], omega_y[150:-150])"
    ]
   },
   {
@@ -305,7 +292,7 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "plt.plot(time[150:-150], omega_y_filtered)"
+    "_ = plt.plot(time[150:-150], omega_y_filtered)"
    ]
   },
   {
@@ -339,7 +326,7 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "plt.plot(freqs, 20*np.log10(abs(response)))"
+    "_ = plt.plot(freqs, 20*np.log10(abs(response)))"
    ]
   },
   {
@@ -357,8 +344,8 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "plt.plot(time[150:-150], omega_y[150:-150])\n",
-    "plt.plot(time[150:-150], omega_y_filtered)"
+    "_ = plt.plot(time[150:-150], omega_y[150:-150])\n",
+    "_ = plt.plot(time[150:-150], omega_y_filtered)"
    ]
   },
   {
@@ -367,8 +354,15 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "plt.plot(fftpack.rfftfreq(omega_y_filtered.shape[0], delta_t)[::2],\n",
-    "         fftpack.rfft(omega_y_filtered)[::2])"
+    "_ = plt.plot(fftpack.rfftfreq(omega_y_filtered.shape[0], delta_t)[::2],\n",
+    "             fftpack.rfft(omega_y_filtered)[::2])"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "### Spectrogram"
    ]
   },
   {
@@ -377,8 +371,17 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "freq, sp_time, Sxx = signal.spectrogram(omega_y, fs=1/delta_t, nperseg=512)\n",
-    "plt.pcolormesh(freq, sp_time, Sxx)"
+    "freq, sp_time, Sxx = signal.spectrogram(omega_y, fs=1/delta_t)\n",
+    "_ = plt.pcolormesh(sp_time, freq, Sxx)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "<div style=\"color: DarkBlue;background-color: LightYellow\">\n",
+    "<i>Exercise:</i> Try to obtain more structure by plotting the logarithm of the spectrogram.\n",
+    "</div>"
    ]
   },
   {
@@ -389,7 +392,7 @@
    "source": [
     "fig = plt.figure()\n",
     "ax = Axes3D(fig)\n",
-    "ax.plot_surface(freq[:, np.newaxis], time, Sxx, cmap=cm.viridis)"
+    "_ = ax.plot_surface(sp_time, freq[:, np.newaxis], Sxx, cmap=cm.viridis)"
    ]
   }
  ],