solutions for assignments 4 and 5 added

This commit is contained in:
Wolfgang Friederich 2025-05-27 13:52:58 +02:00
parent 7d6d611f64
commit 9327a231a9
3 changed files with 939 additions and 0 deletions

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,34 @@
# -------------------------------------------------------------
# Functions for acausal filtering
# -------------------------------------------------------------
import numpy as np
def boxcar_lowpass_filter(fc, df, nf):
"""
Calculates samples of the boxcar lowpass filter transfer function (positive frequencies only)
:param fc: corner frequency in Hz
:param df: frequency stepping
:param nf: number of frequencies
"""
return np.where(df*np.arange(0, nf) > fc, 0, 1)
def boxcar_highpass_filter(fc, df, nf):
"""
Calculates samples of the boxcar highpass filter transfer function (positive frequencies only)
:param fc: corner frequency in Hz
:param df: frequency stepping
:param nf: number of frequencies
"""
return np.where(df*np.arange(0, nf) < fc, 0, 1)
def hann_lowpass_filter(fc, df, nf):
"""
Calculates samples of the Hann filter transfer function (positive frequencies only)
:param fc: corner frequency in Hz
:param df: frequency stepping
:param nf: number of frequencies
"""
f = df*np.arange(0, nf)
return np.where(f < fc, np.cos(np.pi*f/fc)**2, 0)

View File

@ -0,0 +1,466 @@
{
"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
}