{ "cells": [ { "cell_type": "markdown", "id": "0ff7abb1-c698-480b-a894-bdbb5d151fe6", "metadata": {}, "source": [ "## Assignment 5\n", "In this assignment, we look at spectral estimation and study the effects of acausal filters." ] }, { "cell_type": "code", "execution_count": null, "id": "156c8712-11be-4721-9d39-ff2c47a66bcb", "metadata": {}, "outputs": [], "source": [ "%matplotlib inline\n", "import numpy as np\n", "import matplotlib.pyplot as plt\n", "from setupFigure import SetupFigure\n", "from dftFast import dft_fast_coeff, dft_fast_synthesis\n", "from seismicTrace import SeismicTrace" ] }, { "cell_type": "markdown", "id": "f98951f5-96dc-4ef5-b365-85bffa21f700", "metadata": {}, "source": [ "### Task 1: Functions for Hann window and filter transfer functions\n", "Write a function that calculates samples of a Hann window for given sampling interval and window length." ] }, { "cell_type": "code", "execution_count": null, "id": "c36eb15e-26e7-4e6e-bef9-8d566dc07411", "metadata": {}, "outputs": [], "source": [ "def hann_window(dt, tlen):\n", " \"\"\"\n", " Calculate samples of the Hann window for given DT and TLEN.\n", " Number of samples is assumed to be int(tlen/dt)\n", " :param dt: sampling interval\n", " :param tlen: length of window\n", " \"\"\"\n", " ns = int(tlen/dt)\n", " return 2*np.sin(np.pi*dt*np.arange(0, ns)/tlen)**2" ] }, { "cell_type": "markdown", "id": "60dffc7b-17a1-40aa-8a6f-918504e06af2", "metadata": {}, "source": [ "Write a function that calculates samples of the boxcar lowpass filter transfer function for positive frequencies only." ] }, { "cell_type": "code", "execution_count": null, "id": "be77830b-6b2f-4371-9711-e83a1598c363", "metadata": {}, "outputs": [], "source": [ "def boxcar_lowpass_filter(fc, df, nf):\n", " \"\"\"\n", " Calculates samples of the boxcar lowpass filter transfer function (positive frequencies only)\n", " :param fc: corner frequency in Hz\n", " :param df: frequency stepping\n", " :param nf: number of frequencies\n", " \"\"\"\n", " return np.where(df*np.arange(0, nf) > fc, 0, 1)" ] }, { "cell_type": "markdown", "id": "33eb7b1d-4abd-42a2-99ec-cdd1765fbfde", "metadata": {}, "source": [ "Write a function that calculates samples of the boxcar highpass filter transfer function for positive frequencies only." ] }, { "cell_type": "code", "execution_count": null, "id": "29c56269-025a-43e9-8748-c0514a72f70d", "metadata": {}, "outputs": [], "source": [ "def boxcar_highpass_filter(fc, df, nf):\n", " \"\"\"\n", " Calculates samples of the boxcar highpass filter transfer function (positive frequencies only)\n", " :param fc: corner frequency in Hz\n", " :param df: frequency stepping\n", " :param nf: number of frequencies\n", " \"\"\"\n", " return np.where(df*np.arange(0, nf) < fc, 0, 1)" ] }, { "cell_type": "markdown", "id": "937b6b14-4d8f-40ed-bc71-86888d6e726c", "metadata": {}, "source": [ "Write a function that calculates samples of the Hann filter transfer function for positive frequencies only." ] }, { "cell_type": "code", "execution_count": null, "id": "0101ff10-d14d-4b96-83f8-b26f96e5ec8d", "metadata": {}, "outputs": [], "source": [ "def hann_lowpass_filter(fc, df, nf):\n", " \"\"\"\n", " Calculates samples of the Hann filter transfer function (positive frequencies only)\n", " :param fc: corner frequency in Hz\n", " :param df: frequency stepping\n", " :param nf: number of frequencies\n", " \"\"\"\n", " f = df*np.arange(0, nf)\n", " return np.where(f < fc, np.cos(np.pi*f/fc)**2, 0) " ] }, { "cell_type": "markdown", "id": "d641182d-5526-45a9-a69a-0a6965fd942b", "metadata": {}, "source": [ "### Task 2: Spectral estimation and windowing\n", "Here, we first setup a time series that consists of a superposition of different exponentially damped sine functions simulating a series of free oscillations:\n", "\\begin{align}\n", "s(t) = \\sum_{k=0}^N A_k\\sin(2\\pi f_k t)\\,\\exp(-\\pi f_k t/Q_k) \\,.\n", "\\end{align}\n", "The aim is to find the frequencies by Fourier analysis." ] }, { "cell_type": "code", "execution_count": null, "id": "eba91f73-1651-4fd7-ad90-70980b1c5d4d", "metadata": {}, "outputs": [], "source": [ "feig = np.array([0.3, 0.48, 0.64, 0.68, 0.82, 0.85, 0.93, 0.94])*1.e-3 # eigenfreqeuncies in Hz\n", "amp = np.array([1.0, 0.30, 2.00, 0.61, 4.00, 0.53, 3.00, 0.24]) # amplitudes\n", "phi = np.array([ 11, 46, 271, 123, 337, 173, 65, 221])*np.pi/180. # phases\n", "q = np.array([600, 200, 2000, 1000, 600, 100, 800, 900]) # quality factors" ] }, { "cell_type": "markdown", "id": "e455172a-50ad-4aa4-a1b3-d29d50a52b49", "metadata": {}, "source": [ "Write code that impements the above equation using the provided frequencies, amplitudes, phases and Q values. Choose dt=100 s and allow a varaible time series length of tens of hours. Plot the resulting time series." ] }, { "cell_type": "code", "execution_count": null, "id": "c808e6c4-8e39-4e7c-a689-860230ff775a", "metadata": {}, "outputs": [], "source": [ "nhour = 100\n", "dt = 100.0\n", "tlen = 3600*nhour\n", "ns = int(tlen/dt)\n", "print(\"Number of samples: \", ns)\n", "t = dt*np.arange(0, ns)\n", "seis = np.zeros(ns)\n", "for j, f in enumerate(feig):\n", " seis = seis + amp[j]*np.sin(2*np.pi*f*t+phi[j])*np.exp(-np.pi*f*t/q[j])" ] }, { "cell_type": "code", "execution_count": null, "id": "3d0dbe36-34e8-486d-bc63-f5be4aff2d0c", "metadata": {}, "outputs": [], "source": [ "fig1, ax1 = SetupFigure(12, 5, \"Time [h|\", \"s(t)\", \"Ground motion\", 14)\n", "ax1.plot(t/3600, seis, color='blue', ls='-')" ] }, { "cell_type": "markdown", "id": "7ae65e6e-0f73-42fc-b251-878f0ef7f36c", "metadata": {}, "source": [ "Produce a second time series by multiplying the original one with a Hann window and also plot it." ] }, { "cell_type": "code", "execution_count": null, "id": "b62c1a43-f57e-4397-a8f9-7ec813db1e9d", "metadata": {}, "outputs": [], "source": [ "win = hann_window(dt, tlen)\n", "seisw = seis*win" ] }, { "cell_type": "code", "execution_count": null, "id": "41193b3d-110b-42a4-b2ee-9ee0dbd759af", "metadata": {}, "outputs": [], "source": [ "fig2, ax2 = SetupFigure(12, 5, \"Time [h|\", \"swin(t)\", \"Windowed ground motion\", 14)\n", "ax2.plot(t/3600, seisw, color='blue', ls='-')" ] }, { "cell_type": "markdown", "id": "26feab1b-41a6-464f-b578-95814286b915", "metadata": {}, "source": [ "Perform a fast Fourier analysis, calculate the associated frequencies and plot the resulting amplitude spectra of both the original and the windowed time series. Compare the results." ] }, { "cell_type": "code", "execution_count": null, "id": "164cd4c2-72ea-44e8-a236-95475118aad1", "metadata": {}, "outputs": [], "source": [ "spec = dft_fast_coeff(seis)\n", "f = 1./tlen*np.arange(0, spec.size)\n", "print(\"Number of frequencies: \", spec.size, \" fmax = \", f[-1]*1000, \"mHz\")" ] }, { "cell_type": "code", "execution_count": null, "id": "0c88a619-8b9c-44ed-bc5f-f6ecce938af3", "metadata": {}, "outputs": [], "source": [ "specw = dft_fast_coeff(seisw)" ] }, { "cell_type": "code", "execution_count": null, "id": "73b8fae2-e712-47d7-99a5-ebe5375084cf", "metadata": {}, "outputs": [], "source": [ "fig3, ax3 = SetupFigure(12, 5, \"Frequency [mHz]\", \"S(f)\", \"Amplitude spectrum of ground motion\", 14)\n", "ax3.set_xlim(0,1.2)\n", "ax3.plot(f*1000, np.absolute(spec), color='blue', ls='-', label='boxcar')\n", "ax3.plot(f*1000, np.absolute(specw), color='red', ls='-', label='hann')\n", "ax3.legend()" ] }, { "cell_type": "markdown", "id": "689b3af4-1146-4041-9f4e-baed098d2a23", "metadata": {}, "source": [ "### Task 2: Read a seismogram from file and compute spectrum\n", "Here, we first read a recorded seismogram form a file, do a Fourier analysis and later apply diverse acausal filters. We make use of the class \"SeismicTrace\" that defines a data structure for a seismic trace and also some useful functions related to traces. You may want to look at the provided code in \"seismicTrace.py\"." ] }, { "cell_type": "code", "execution_count": null, "id": "912ae1f7-8cbc-4446-9ef0-b446b57d789d", "metadata": {}, "outputs": [], "source": [ "seisfile = \"CH.PANIX.HHZ\"\n", "with open(seisfile, 'r') as fd:\n", " seistrace = SeismicTrace.readFromAscii(fd)\n", "t = seistrace.tanf+seistrace.dt*np.arange(0, seistrace.nsamp)\n", "seistrace.printInfo()" ] }, { "cell_type": "code", "execution_count": null, "id": "eaee9262-f577-49a1-acee-9b9c27f996be", "metadata": {}, "outputs": [], "source": [ "fig4, ax4 = SetupFigure(12, 5, \"Time [s]\", \"s(t)\", \"Unfiltered seismic data\", 14)\n", "ax4.plot(t, seistrace.vals, color='blue', ls='-')" ] }, { "cell_type": "markdown", "id": "934a4fa1-7f8d-4977-bf86-bfe094abc5e5", "metadata": {}, "source": [ "Perform a fast Fourier analysis, calculate the associated frequencies and plot the resulting amplitude spectrum." ] }, { "cell_type": "code", "execution_count": null, "id": "c4a0c8c9-b51a-4116-9988-73d75b2f6704", "metadata": {}, "outputs": [], "source": [ "spec = dft_fast_coeff(seistrace.vals)\n", "f = 1./seistrace.tlen*np.arange(0, spec.size)\n", "print(\"Number of frequencies: \", spec.size, \" fmax = \", f[-1], \"Hz\")" ] }, { "cell_type": "code", "execution_count": null, "id": "82fb96d5-39ac-4c6a-a360-3544167ec26b", "metadata": {}, "outputs": [], "source": [ "fig5, ax5 = SetupFigure(12, 5, \"Frequency [Hz]\", \"S(f)\", \"Amplitude spectrum of seismogram\", 14)\n", "ax5.set_xlim(0, 1)\n", "ax5.plot(f, np.absolute(spec), color='blue', ls='-', label='boxcar')" ] }, { "cell_type": "markdown", "id": "8d36d0c3-6faf-4e0b-b934-8822100b2b2f", "metadata": {}, "source": [ "### Task 3: Acausal low pass filtering" ] }, { "cell_type": "markdown", "id": "36b37ff1-3080-4651-a344-9cb4ede9109d", "metadata": {}, "source": [ "Apply the boxcar lowpass filter to the data and plot the result." ] }, { "cell_type": "code", "execution_count": null, "id": "a1e59a76-d251-4d66-b6ba-7c29a0626b3e", "metadata": {}, "outputs": [], "source": [ "fc = 0.1\n", "df = 1./seistrace.tlen\n", "specfil = spec*boxcar_lowpass_filter(fc, df, spec.size)\n", "seisfil = dft_fast_synthesis(specfil)" ] }, { "cell_type": "code", "execution_count": null, "id": "532ad374-2728-435d-8dca-afd7fcf9c475", "metadata": {}, "outputs": [], "source": [ "fig6, ax6 = SetupFigure(12, 5, \"Time [s]\", \"sf(t)\", \"Boxcar lowpass filtered seismic data, FC = {:5.2f} Hz\".format(fc), 14)\n", "#ax6.set_xlim(64000, 64500)\n", "ax6.plot(t, seisfil, color='blue', ls='-')" ] }, { "cell_type": "markdown", "id": "e1097e6d-c459-489d-aecd-75911419a319", "metadata": {}, "source": [ "Apply the Hann low pass filter to the data and plot the result." ] }, { "cell_type": "code", "execution_count": null, "id": "be2e1d6d-0c4e-4bfa-8612-ce626393bdff", "metadata": {}, "outputs": [], "source": [ "specfil2 = spec*hann_lowpass_filter(fc, df, spec.size)\n", "seisfil2 = dft_fast_synthesis(specfil2)" ] }, { "cell_type": "code", "execution_count": null, "id": "b5d4bf72-9bc2-4679-9178-4f7bc650e4ee", "metadata": {}, "outputs": [], "source": [ "fig7, ax7 = SetupFigure(12, 5, \"Time [s]\", \"sf(t)\", \"Hann lowpass filtered seismic data, FC = {:5.2f}\".format(fc), 14)\n", "#ax7.set_xlim(64000, 64500)\n", "ax7.plot(t, seisfil2, color='blue', ls='-')" ] }, { "cell_type": "markdown", "id": "3bbec473-2bf7-41d0-828d-9413dff147e8", "metadata": {}, "source": [ "Apply the boxcar highpass filter to the data and plot the result." ] }, { "cell_type": "code", "execution_count": null, "id": "c9ec91f3-cc1c-48e5-ac53-882002fc74f6", "metadata": {}, "outputs": [], "source": [ "specfil3 = spec*boxcar_highpass_filter(fc, df, spec.size)\n", "seisfil3 = dft_fast_synthesis(specfil3)" ] }, { "cell_type": "code", "execution_count": null, "id": "7a24950e-8438-4e3b-903e-75f2f4408105", "metadata": {}, "outputs": [], "source": [ "fig8, ax8 = SetupFigure(12, 5, \"Time [s]\", \"sf(t)\", \"Boxcar highpass filtered seismic data, FC = {:5.2f}\".format(fc), 14)\n", "#ax7.set_xlim(64000, 64500)\n", "ax8.plot(t, seisfil3, color='blue', ls='-')" ] }, { "cell_type": "code", "execution_count": null, "id": "92e0ebc5-9f7d-4302-8ad1-9aed6c7f2d6a", "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.12.4" } }, "nbformat": 4, "nbformat_minor": 5 }