Add all notebooks for part ii of the lecture. (#13)
Reviewed-on: #13 Co-authored-by: Janis Heuel <janis.heuel@ruhr-uni-bochum.de> Co-committed-by: Janis Heuel <janis.heuel@ruhr-uni-bochum.de>
This commit is contained in:
parent
1a077cb628
commit
a47d87bc9b
230
04-FFT_DFT_and_Applications/resample.ipynb
Normal file
230
04-FFT_DFT_and_Applications/resample.ipynb
Normal file
@ -0,0 +1,230 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Resampling of Time Series\n",
|
||||
"## Decimation\n",
|
||||
"The Discrete Fourier Transform (DFT) can be used for some very basic signal processing operations. We can either use is for decimation, which results in redcution of the number of samples of a given time series or we can use it for interpolation, which is the opposite of decimation.\n",
|
||||
"Decimation implies a reduction of time resolution and thus a decrease in the Nyquist frequency.\n",
|
||||
"If the original signal contains higher frequency than the new Nyquist frequency\n",
|
||||
"(after decimation) we get aliasing. Thus, a low pass filtering to the new Nyquist\n",
|
||||
"frequency is required before decimation. Decimation and low-pass filtering can be\n",
|
||||
"achieved in one step by using the DFT:\n",
|
||||
"1. Transform the time series to the frequency domain using the DFT.\n",
|
||||
"2. Reduce the Nyquist frequency by the desired factor (when using the Fast Fourier Transform, this should be a power of 2), and apply an appropriate taper. That’s the low-pass filter!\n",
|
||||
"3. Do an inverse DFT of the modified spectrum with reduced Nyquist frequency and obtain a time series with greater sampling interval (because lower Nyquist frequency implies lower time resolution)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Import all required packages\n",
|
||||
"%matplotlib inline\n",
|
||||
"import os\n",
|
||||
"import numpy as np\n",
|
||||
"import matplotlib.pyplot as plt\n",
|
||||
"from obspy import read\n",
|
||||
"from scipy import signal"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Read the data\n",
|
||||
"datapath = os.path.join(os.path.expanduser('~'),'work', 'data', 'DOR50', '*063')\n",
|
||||
"st = read(datapath)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def decimate(x, dt, factor):\n",
|
||||
" \"\"\"\n",
|
||||
" Easy decimation for real input data x. Note, since new_dt might not be old_dt * 2**n,\n",
|
||||
" the decimation is not correct.\n",
|
||||
" x: Data array\n",
|
||||
" dt: sampling rate of x\n",
|
||||
" factor: factor for decimation, thus new sampling rate is dt/factor\n",
|
||||
" \"\"\"\n",
|
||||
" # 1. Transform x to frequnecy domain\n",
|
||||
" ft = np.fft.rfft(x)\n",
|
||||
" freqs = np.fft.rfftfreq(n=len(x), d=dt)\n",
|
||||
" \n",
|
||||
" # 2. Get Nyquist Frequencies for both old dt an new dt\n",
|
||||
" \n",
|
||||
" \n",
|
||||
" # Find index of new Nyquist Frequnency in freqs\n",
|
||||
" index = np.where(freqs > new_f_ny)[0][0]\n",
|
||||
" \n",
|
||||
" # 3. Apply window function as lowpass filter and do inverse DFT \n",
|
||||
" new_ft = ft[:index]\n",
|
||||
" new_ft = new_ft * signal.tukey(len(new_ft), alpha=0.25)\n",
|
||||
" x_new = np.fft.irfft(ft[:index])\n",
|
||||
" \n",
|
||||
" return x_new, dt*factor"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Plot results for original and decimated data\n",
|
||||
"# Read data from st\n",
|
||||
"data = st[0].data[:100000]\n",
|
||||
"\n",
|
||||
"# Apply Decimation\n",
|
||||
"dec, dt_dec = decimate(data, dt=st[0].stats.delta, factor=2)\n",
|
||||
"\n",
|
||||
"# Create arrays for time to plot\n",
|
||||
"t_data = np.arange(0, len(data)) * st[0].stats.delta\n",
|
||||
"t_dec = np.arange(0, len(dec)) * dt_dec\n",
|
||||
"\n",
|
||||
"# Create plot widget\n",
|
||||
"plt.plot(t_data, data - np.mean(data), alpha=0.5)\n",
|
||||
"plt.plot(t_dec, dec - np.mean(dec), alpha=0.5)\n",
|
||||
"plt.xlabel(\"Time (s)\")\n",
|
||||
"plt.ylabel(\"Amplitude (a.u.)\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Interpolation\n",
|
||||
"The opposite of decimation is interpolation. Here, we want a finer sampling of the\n",
|
||||
"time series without changing the frequency content. We could do this in the time\n",
|
||||
"domain by some interpolation rule using neighbouring samples. We can also do it\n",
|
||||
"by using the DFT:\n",
|
||||
"1. Transform the time series to the frequency domain using the DFT.\n",
|
||||
"2. Append zeros to the spectrum thus increasing the Nyquist frequency.\n",
|
||||
"3. Do an inverse DFT of the extended spectrum and obtain a time series with smaller sampling interval (because higher Nyquist frequency implies higher time resolution). The frequency content is unchanged!"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def interpolation(x, dt, new_dt):\n",
|
||||
" \"\"\"\n",
|
||||
" Easy function for interpolation of a given data array to increase the the sampling rate.\n",
|
||||
" \n",
|
||||
" x: data array\n",
|
||||
" dt: sampling rate\n",
|
||||
" new_dt: new sampling rae\n",
|
||||
" \"\"\"\n",
|
||||
" # 1. Transform x to frequnecy domain\n",
|
||||
" ft = np.fft.rfft(x)\n",
|
||||
" \n",
|
||||
" # Determine the number of zeros to add\n",
|
||||
" \n",
|
||||
" \n",
|
||||
" # 2. Append zeros to spectrum\n",
|
||||
" ft = np.concatenate((ft, np.zeros(append_zeros)))\n",
|
||||
" \n",
|
||||
" # 3. Do inverse DFT and determine new dt\n",
|
||||
" x_new = np.fft.irfft(ft)\n",
|
||||
" new_dt = t_max / len(x_new)\n",
|
||||
" \n",
|
||||
" return x_new, new_dt"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Create plot\n",
|
||||
"# Read data from st\n",
|
||||
"data = st[0].data[:100000]\n",
|
||||
"\n",
|
||||
"# Apply interpolation\n",
|
||||
"interp, dt_interp = interpolation(data, dt=st[0].stats.delta, new_dt=1/150)\n",
|
||||
"\n",
|
||||
"# Create time arrays\n",
|
||||
"t_data = np.arange(0, len(data)) * st[0].stats.delta\n",
|
||||
"t_interp = np.arange(0, len(interp)) * dt_interp\n",
|
||||
"\n",
|
||||
"# Create plot widget\n",
|
||||
"plt.plot(t_data, data - np.mean(data), alpha=0.5)\n",
|
||||
"plt.plot(t_interp, interp - np.mean(interp), alpha=0.5)\n",
|
||||
"plt.xlabel(\"Time (s)\")\n",
|
||||
"plt.ylabel(\"Amplitude (a.u.)\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Comparison with function scipy.signal.resample\n",
|
||||
"For documentation see https://docs.scipy.org/doc/scipy/reference/generated/scipy.signal.resample.html"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Import funtion\n",
|
||||
"from scipy.signal import resample\n",
|
||||
"\n",
|
||||
"# Read data from st and create time array\n",
|
||||
"data = st[0].data[:100000]\n",
|
||||
"time = np.arange(0, len(data)) * st[0].stats.delta\n",
|
||||
"\n",
|
||||
"# Apply resample funtion\n",
|
||||
"resampled_x, resampled_t = resample(data, num=int(len(data)*2), t=time)\n",
|
||||
"\n",
|
||||
"# Create plot\n",
|
||||
"plt.plot(time, data - np.mean(data), alpha=0.5)\n",
|
||||
"plt.plot(resampled_t, resampled_x - np.mean(resampled_x), alpha=0.5)\n",
|
||||
"plt.xlabel(\"Time (s)\")\n",
|
||||
"plt.ylabel(\"Amplitude (a.u.)\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"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.6.7"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 2
|
||||
}
|
307
05-Spectrogram/multipleFilterTechnique.ipynb
Normal file
307
05-Spectrogram/multipleFilterTechnique.ipynb
Normal file
@ -0,0 +1,307 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Multiple filter technique"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"An alternative to the moving window method is the multiple filter technique. Instead of moving a window in the time domain, a window is moved in the frequency domain. Of course, a window in the frequency domain is a filter, and hence the name of the method. Let $H_k(\\omega,\\omega_k)$ be the transfer function of the $k$-th filter centered on frequency $\\omega_k$. Applying the filter involves multiplication of the filter transfer function with the Fourier transform of the time signal and then performing an inverse Fourier transform:\n",
|
||||
"\\begin{align}\n",
|
||||
"f_k(t) = \\int_{-\\infty}^\\infty\\,H_k(\\omega,\\omega_k)F(\\omega)\\exp(i\\omega t)\\frac{d\\omega}{2\\pi} \\,.\n",
|
||||
"\\end{align}\n",
|
||||
"Basically, one could plot the resulting time series against the associated center frequency of the filter. However, $f_k(t)$ can also assume negative values. It would be better to plot the envelope of $f_k(t)$.\n",
|
||||
"\n",
|
||||
"### The analytic signal\n",
|
||||
"This can be obtained by constructing the analytic signal $z_k(t)$ of $f_k(t)$. It is a complex\n",
|
||||
"signal whose real part is the original signal and whose imaginary part is its Hilbert transform. Its absolute\n",
|
||||
"value is the envelope or instantaneous amplitude and its phase is the instantaneous phase:\n",
|
||||
"\\begin{align}\n",
|
||||
"z_k(t) = a_k(t)\\exp(i\\Phi_k(t)) = f_k(t)+iq_k(t) \\ \\mathrm{with}\\ q_k(t) = HT\\{f_k(t)\\}\\,,\n",
|
||||
"\\end{align}\n",
|
||||
"and\n",
|
||||
"\\begin{align}\n",
|
||||
"a_k(t) = \\sqrt{f_k(t)^2+q_k(t)^2},\\ \\Phi_k(t) = \\arctan\\left(\\frac{q_k(t)}{f_k(t)}\\right) \\,.\n",
|
||||
"\\end{align}\n",
|
||||
"\n",
|
||||
"The Hilbert transform can again easily be obtained because its Fourier transform is:\n",
|
||||
"\\begin{align}\n",
|
||||
"Q_k(\\omega) = i\\,\\mathrm{sign}(\\omega)F_k(\\omega)\\,.\n",
|
||||
"\\end{align}\n",
|
||||
"where $F_k(\\omega)$ is the Fourier transform of $f_k(t)$. Inverse Fourier transform of $Q_k(\\omega)$ gives the Hilbert transform of $f_k(t)$.\n",
|
||||
"\n",
|
||||
"### Work flow\n",
|
||||
"So the work flow of the multiple filter technique is as follows:\n",
|
||||
"1. Compute Fourier transform of $f(t)$: $F(\\omega)$.\n",
|
||||
"2. Multiply $F(\\omega)$ with bandpass filters $H_k(\\omega,\\omega_k)$ to obtain $F_k(\\omega)$.\n",
|
||||
"3. Compute Fourier transform of the Hilbert transform: $Q_k(\\omega)=iF_k(\\omega)$.\n",
|
||||
"4. Transform both back to the time domain.\n",
|
||||
"5. Calculate the instantaneous amplitude $a_k(t)$ and phase $\\Phi_k(t)$.\n",
|
||||
"6. Plot $a_k(t)$ in the time-frequency plane.\n",
|
||||
"\n",
|
||||
"### Gaussian filters\n",
|
||||
"One frequently made choice for the bandpass filters is a Gaussian function of the form:\n",
|
||||
"\\begin{align}\n",
|
||||
"H_k(\\omega,\\omega_k) = e^{-\\alpha\\left(\\frac{\\omega-\\omega_k}{\\omega_k}\\right)^2}\n",
|
||||
"\\end{align}\n",
|
||||
"with inverse Fourier transform (impulse response):\n",
|
||||
"\\begin{align}\n",
|
||||
"h_k(t) = \\frac{\\sqrt{\\pi}\\omega_k}{2\\alpha}e^{-\\frac{\\omega_k^2t^2}{4\\alpha}}\\cos\\omega_k t \\,.\n",
|
||||
"\\end{align}\n",
|
||||
"This Fourier transform pair demonstrates a fundamental property of spectral analysis:\n",
|
||||
"Choosing $\\alpha$ large gives a very narrow-banded filter but a very\n",
|
||||
"long impulse response. Choosing $\\alpha$ small gives a wide-banded filter but a very short \n",
|
||||
"impulse response. This is an expression of the uncertainty principle. If the frequency of a signal\n",
|
||||
"is known very well, the signal is distributed over time. On the other hand, an impulse in the time domain has\n",
|
||||
"a constant spectrum, i.e. the frequency is entirely unknown. In quantum mechanics, there is the energy-time\n",
|
||||
"uncertainty relation, where energy $E=\\hbar\\omega$.\n",
|
||||
"\n",
|
||||
"### Time and frequency resolution\n",
|
||||
"The Gaussian Fourier transform pair has a special property. If we measure the duration of the impulse response by\n",
|
||||
"\\begin{align}\n",
|
||||
"D_t^2 = \\int_{-\\infty}^\\infty t^2 |h(t)|^2 dt\n",
|
||||
"\\end{align}\n",
|
||||
"and the spread of the filter transfer function by\n",
|
||||
"\\begin{align}\n",
|
||||
"D_\\omega^2 = \\int_{-\\infty}^\\infty \\omega^2 |H(\\omega)|^2 \\frac{d\\omega}{2\\pi}\\,,\n",
|
||||
"\\end{align}\n",
|
||||
"then the product\n",
|
||||
"\\begin{align}\n",
|
||||
"D_t D_\\omega \\ge \\frac{1}{2} \\,.\n",
|
||||
"\\end{align}\n",
|
||||
"For the Gaussian filter, equality holds. Hence, the Gaussian filter is the one with the\n",
|
||||
"best compromise between bandwidth and duration."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"scrolled": true
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Preparation: load packages\n",
|
||||
"import os\n",
|
||||
"from obspy.core import read\n",
|
||||
"from obspy.core import UTCDateTime\n",
|
||||
"import numpy as np\n",
|
||||
"import matplotlib.pylab as plt"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"scrolled": true
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Multiple filter analysis\n",
|
||||
"def multipleFilterAnalysis(data,alfa,cfreq,dt,ndec):\n",
|
||||
" \"\"\"\n",
|
||||
" Perform a multiple filter analysis of data.\n",
|
||||
" data: Array of detrended and demeaned data whose length is power of 2 \n",
|
||||
" alfa: Width parameter of Gaussian bandpass filter\n",
|
||||
" cfreq: Array of center frequencies of Gaussian filter\n",
|
||||
" dt: sampling interval of data\n",
|
||||
" ndec: decimation factor for instantaneous amplitude output\n",
|
||||
" \"\"\"\n",
|
||||
" npts = len(data)\n",
|
||||
" nd = int(pow(2, np.ceil(np.log(npts)/np.log(2)))) # find next higher power of 2 of npts\n",
|
||||
" ftd = np.fft.rfft(data,nd) # Fourier transform of entire data set (pos. freq.)\n",
|
||||
" # data are padded with zeros since npts <= nd\n",
|
||||
" freq = np.fft.rfftfreq(nd,dt) # Fourier frequencies (positive frequencies)\n",
|
||||
" jf = 0 # center frequency counter\n",
|
||||
" mfa = np.zeros((len(cfreq),npts//ndec+1)) # numpy array for MFA result\n",
|
||||
" for cf in cfreq:\n",
|
||||
" hg = np.exp(-alfa*((freq-cf)/cf)**2) # Gaussian filter (use f instead of omega here)\n",
|
||||
" fk = hg*ftd # multiply FT of data with filter\n",
|
||||
" qk = np.complex(0,1)*fk # FT of Hilbert transform\n",
|
||||
" ftk = np.fft.irfft(fk) # filtered data\n",
|
||||
" qtk = np.fft.irfft(qk) # Hilbert transform of filtered data\n",
|
||||
" at = np.sqrt(ftk**2+qtk**2) # instantaneous amplitude\n",
|
||||
" mfa[jf,:] = at[0:npts:ndec] # store decimated original result\n",
|
||||
" jf = jf+1 # increase center frequency count\n",
|
||||
" return mfa"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# normalize multiple filter result either along time or frequency axis\n",
|
||||
"def normalizeMFT(mfa,mode,exp):\n",
|
||||
" \"\"\"\n",
|
||||
" Normalize the result of the mutiple filtering operation.\n",
|
||||
" mfa: array with instantaneous amplitudes versus frequency (mfa(f,t))\n",
|
||||
" mode: normalization mode: if 'time', normalize along time axis\n",
|
||||
" else normalize along frequency axis\n",
|
||||
" exp: exponent for modifying inst amp using a power less than 1\n",
|
||||
" \"\"\"\n",
|
||||
" nf,nt = mfa.shape\n",
|
||||
" if mode == 'time':\n",
|
||||
" for jf in range(0,nf):\n",
|
||||
" mfamax = np.amax(mfa[jf,:])\n",
|
||||
" mfa[jf,:] = np.power(mfa[jf,:]/mfamax+1.e-10,exp) \n",
|
||||
" else:\n",
|
||||
" for jt in range(0,nt):\n",
|
||||
" mfamax = np.amax(mfa[:,jt])\n",
|
||||
" mfa[:,jt] = np.power(mfa[:,jt]/mfamax+1.e-10,exp)\n",
|
||||
" return mfa"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"scrolled": true
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# borehole data from stations, Jan 23 2013\n",
|
||||
"station = 'GEO2' # station code\n",
|
||||
"# available stations are GEO2, GEO3 (only 14:00), GEO4, GEO5, GEO6, GEO7\n",
|
||||
"\n",
|
||||
"record = '1400' # record starting at 1400\n",
|
||||
"stime = UTCDateTime('2013-01-23 14:17:00Z') # use record starting at 1400\n",
|
||||
"etime = UTCDateTime('2013-01-23 14:19:00Z')\n",
|
||||
"ttitle = ', depth: 56.5 m'\n",
|
||||
"\n",
|
||||
"# search string for database\n",
|
||||
"datapath = os.path.join(os.path.expanduser('~'),'work', 'data', 'GE-stations', station, 'e*'+record+'*.HHZ')\n",
|
||||
"st = read(datapath) # read file using obspy read\n",
|
||||
"print(st)\n",
|
||||
"\n",
|
||||
"st.trim(stime,etime) # trim data stream to desired start and end time\n",
|
||||
"st.detrend('linear') # do a linear detrending\n",
|
||||
"st.detrend('demean') # subtract mean value\n",
|
||||
"tr = st[0] # extract first trace from stream (there is only one)\n",
|
||||
"tr.plot(); # plot trace"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# prepare multiple filtering\n",
|
||||
"alfa = 1000.0 # filter width parameter\n",
|
||||
"npts = tr.stats.npts # number of samples in time series\n",
|
||||
"dt = tr.stats.delta # sampling interval\n",
|
||||
"fmax = 1./(2.*dt) # Nyquist frequency\n",
|
||||
"nd = int(pow(2, np.ceil(np.log(npts)/np.log(2)))) # find next higher power of 2 of npts\n",
|
||||
"print(\"Number of samples: \",npts, # print some information about time series\n",
|
||||
" \"\\nSampling interval: \",dt,\n",
|
||||
" \"\\nNumber of samples with padding: \",nd,\n",
|
||||
" \"\\nMax. frequency: \",fmax)\n",
|
||||
"cfreq = np.linspace(1.,fmax,1000) # array of filter center frequencies\n",
|
||||
"freq = np.fft.rfftfreq(nd,dt)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"scrolled": true
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"ndec = 100 # decimation factor for inst amplitude\n",
|
||||
"mfa = multipleFilterAnalysis(tr.data,alfa,cfreq,dt,ndec)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"mfa = normalizeMFT(mfa,'freq',0.5)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# plot the result\n",
|
||||
"extent = (0.,npts*dt,fmax,1.) # extent of matrix in true time and frequency\n",
|
||||
"plt.figure(figsize = [15,6])\n",
|
||||
"plt.imshow(mfa,extent = extent,aspect=0.25) # do plotting\n",
|
||||
"plt.xlabel('Time [s]')\n",
|
||||
"plt.ylabel('Center frequency [Hz]')\n",
|
||||
"plt.show()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# plot Gaussian filters\n",
|
||||
"cf = 0.5*fmax\n",
|
||||
"plt.figure(figsize = [15,4])\n",
|
||||
"for alfa in [10,100,1000,10000,100000]:\n",
|
||||
" hg = np.exp(-alfa*((freq-cf)/cf)**2) # Gaussian filter (use f instead of omega here)\n",
|
||||
" plt.plot(freq,hg)\n",
|
||||
"plt.show()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Exercise"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"1. Study the effect of $\\alpha$ on the result of the multiple filtering\n",
|
||||
"2. Think about the numerical effort of MFT in comparison with the moving window technique\n",
|
||||
"3. Perform the analysis for different stations\n",
|
||||
"4. Do you have some ideas why the results look different? What role plays normalization? Try to change the code such that each spectrum is normalized and not each instantaneous amplitude series."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"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.6.7"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 2
|
||||
}
|
165
05-Spectrogram/nonlinear_thresholding.ipynb
Normal file
165
05-Spectrogram/nonlinear_thresholding.ipynb
Normal file
@ -0,0 +1,165 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Nonlinear Thresholding\n",
|
||||
"When noise and signal share a common frequency band, spectral filtering would lead to a loss in signal. For these cases, we need different techniques to reduce the disturibing noise. We start with a reocorde signal $x(t)$, contains some signal $s(t)$ and additive noise $n(t)$:\n",
|
||||
"$$\n",
|
||||
"x(t) = s(t) + n(t)\n",
|
||||
"$$\n",
|
||||
"We can rewrite the equation above in time-frequency domain as\n",
|
||||
"$$\n",
|
||||
"X(t, f) = S(t, f) + N(t, f) ~.\n",
|
||||
"$$\n",
|
||||
"Assuming that some time-frequnecy coefficients can be associated by noise, we set all coefficients, which a below this threshold, to zero:\n",
|
||||
"$$\n",
|
||||
"\\tilde{X}(t, f) = \\left\\{\n",
|
||||
" \\begin{array}{@{}ll@{}}\n",
|
||||
" X(t, f) & \\mathrm{if}~ |X(t, f)| \\geq \\beta(f) \\\\\n",
|
||||
" 0 & \\mathrm{otherwise}\n",
|
||||
" \\end{array}\\right.\n",
|
||||
" ~,\n",
|
||||
"$$\n",
|
||||
"where $\\beta(f)$ denotes the threshold function. The threshold function is defined as \n",
|
||||
"$$\n",
|
||||
"\\beta(f) = \\mathrm{ECDF}_f^{-1} (P = 0.99) ~,\n",
|
||||
"$$\n",
|
||||
"where $\\mathrm{ECDF}_f^{-1}$ denotes the inverse cumulative distribution function or quantile function, e.g. before the first arrival of earthquake waves."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Load all required packages\n",
|
||||
"import os\n",
|
||||
"import numpy as np\n",
|
||||
"from scipy.signal import stft, istft\n",
|
||||
"import matplotlib.pyplot as plt"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Load our earthquake data and plot\n",
|
||||
"d = np.load(os.path.join(os.path.expanduser('~'),'work', 'data', 'events', 'bug2019mgoh_Z.npz'))\n",
|
||||
"#d = np.load(os.path.join(os.path.expanduser('~'),'work', 'data', 'events', 'bug2019gbbo_Z.npz'))\n",
|
||||
"#d = np.load(os.path.join(os.path.expanduser('~'),'work', 'data', 'events', 'bug2019ibsd_Z.npz'))\n",
|
||||
"plt.figure(figsize=(15, 8))\n",
|
||||
"plt.plot(d['data']);"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Compute spectrogram of data\n",
|
||||
"f, t, X = stft(d['data'], fs=1/0.01, nfft=198, nperseg=99) # Be careful with choice of nfft and nperseg, \n",
|
||||
" # because istft does not work for all pairs\n",
|
||||
"print(X.shape)\n",
|
||||
"plt.figure(figsize=(15, 8))\n",
|
||||
"plt.pcolormesh(t, f, np.abs(X))\n",
|
||||
"plt.xlabel(\"Frequency (Hz)\")\n",
|
||||
"plt.ylabel(\"Time (s)\");"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Functions for thresholding\n",
|
||||
"def threshold(X, quantile=0.99):\n",
|
||||
" \"\"\"\n",
|
||||
" X: time-frequency coefficients\n",
|
||||
" q: quantile [0-1]\n",
|
||||
" \"\"\"\n",
|
||||
" # Loop over frequencies to build threshold function\n",
|
||||
" beta = np.zeros(X.shape[0])\n",
|
||||
" for i in range(X.shape[0]):\n",
|
||||
" beta[i] = np.quantile(np.abs(X[i, :]), q=quantile)\n",
|
||||
" \n",
|
||||
" return beta\n",
|
||||
"\n",
|
||||
"def modify_spectrogram(X, beta):\n",
|
||||
" # Loop over all items in X and apply threshold\n",
|
||||
" # Task: modify X!\n",
|
||||
" \n",
|
||||
" return X"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Compute STFT of data before first arrival of P\n",
|
||||
"_, _, X_thres = stft(d['data'][:2500], fs=1/0.01, nfft=198, nperseg=99)\n",
|
||||
"# Estimate threshold fucntion\n",
|
||||
"beta = threshold(X_thres)\n",
|
||||
"\n",
|
||||
"# Modifiy original spectrogram\n",
|
||||
"X_mod = modify_spectrogram(X, beta)\n",
|
||||
"\n",
|
||||
"# Plot modified spectrogram\n",
|
||||
"plt.figure(figsize=(15, 8))\n",
|
||||
"plt.pcolormesh(t, f, np.abs(X_mod))\n",
|
||||
"plt.xlabel(\"Frequency (Hz)\")\n",
|
||||
"plt.ylabel(\"Time (s)\");"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Inverse STFT of modified spectrogram\n",
|
||||
"t, x_mod = istft(X_mod, fs=1/0.01, nfft=198, nperseg=99)\n",
|
||||
"\n",
|
||||
"# Plot corrected seismogram\n",
|
||||
"plt.figure(figsize=(15, 8))\n",
|
||||
"plt.plot(t, x_mod);"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"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.6.7"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 2
|
||||
}
|
272
05-Spectrogram/time_frequency_analysis.ipynb
Normal file
272
05-Spectrogram/time_frequency_analysis.ipynb
Normal file
@ -0,0 +1,272 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Time Frequency Analysis\n",
|
||||
"## Moving window analysis\n",
|
||||
"One way to analyse the time-varying frequency content of a signal is to\n",
|
||||
"apply windows in the time domain to the signal and to calculate a Fourier spectrum\n",
|
||||
"of the windowed part. The window marches along the signal with defined overlap creating\n",
|
||||
"a series of Fourier spectra associated with the center times of the windows. The resulting amplitude\n",
|
||||
"spectra are the plotted versus window center time. In more detail:\n",
|
||||
"\n",
|
||||
"1. Choose windowing functions: $w(t,t_m)$ with $t_m$ the center of the window.\n",
|
||||
"2. Multiply windowing function with time series: $f_m(t) = f(t)w(t,t_m)$\n",
|
||||
"3. Detrend the windowed signal.\n",
|
||||
"4. Perform a DFT: $F_{km} = \\Delta t\\sum_{n=0}^N f_m(t)\\exp(-2\\pi i \\frac{kn}{N})$\n",
|
||||
" and calculate the absolute value, $|F_{km}|$.\n",
|
||||
"5. Plot the resulting matrix: $|F_{km}|$ in the time-frequency plane."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Borehole Data and Test Site\n",
|
||||
"\n",
|
||||
"Within the scope of constructing new buildings for the International Geothermal Center in Bochum in 2013 wells were drilled for the installation of a geothermal heat exchanger. Bore holes were drilled next to the newly constructed building (Station GEO3). A downhole hammer was used with a diameter of 152 mm. The drill bit operates by water flushing through the drill rod. The water flow rate determines the working frequency of the hammer.\n",
|
||||
"\n",
|
||||
"To observe the drill bit noise a temporarily operating 2-D seismic network was installed around the drill site. Here, the noise of the used downhole hammer is investigated. An array of 16 seismological stations was installed in the test site. Four Mark L-4C-3D 1 Hz sensors, eight S-13 1 Hz sensors, one GS-13 1 Hz sensor and two Güralp CMG-3ESPC broad-band 120 sec – 50 Hz sensors were in use. Additionally an accelerometer was fixed to the drill rod (GEO11, blue triangle).\n",
|
||||
"\n",
|
||||
"Some of the stations were positioned within one of the infrastructure tunnels servicing the university containing water conduits, long-distance heat line and electric cables (e.g. Station GEO4 and GEO05). Thus, noise could be reduced that might disturb the recordings. Other stations are located within buildings (e.g. Station GEO2 and GEO03) ore outside (e.g. Station GEO6 and GEO07).\n",
|
||||
"\n",
|
||||
"![Map ot the stations at GZB](../images/karte3.jpg)\n",
|
||||
"\n",
|
||||
"Drill bit noise was recorded up to the maximum drilling depth of 200 m. A drilling cycle is characterised by switching on the water pump, followed by the drilling with higher amplitude signals, that lasts several minutes. The water pump\n",
|
||||
"is stopped about 5 to 15 minutes after the drilling finished depending on the drill depth."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Preparation: load packages\n",
|
||||
"import os\n",
|
||||
"from obspy.core import read\n",
|
||||
"from obspy.core import UTCDateTime\n",
|
||||
"import numpy as np\n",
|
||||
"import matplotlib.pylab as plt"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# data from stations, Jan 23 2013\n",
|
||||
"\n",
|
||||
"record = '1400' # either use record starting at 1300 or 1400\n",
|
||||
"station = 'GEO7' # station code\n",
|
||||
"# available stations are GEO2, GEO3 (only 14:00), GEO4, GEO5, GEO6, GEO7\n",
|
||||
"\n",
|
||||
"if record =='1300':\n",
|
||||
" stime = UTCDateTime('2013-01-23 13:16:00Z') # use record starting at 1300\n",
|
||||
" etime = UTCDateTime('2013-01-23 13:25:00Z')\n",
|
||||
" ttitle = ', depth: 36.5 m'\n",
|
||||
"else:\n",
|
||||
" stime = UTCDateTime('2013-01-23 14:17:00Z') # use record starting at 1400 (14:14-14:23)\n",
|
||||
" etime = UTCDateTime('2013-01-23 14:19:00Z')\n",
|
||||
" ttitle = ', depth: 56.5 m'\n",
|
||||
" \n",
|
||||
"datapath = os.path.join(os.path.expanduser('~'),'work', 'data','GE-stations', station, 'e*'+record+'*.HHZ') # search string for database\n",
|
||||
"st = read(datapath) # read file using obspy read\n",
|
||||
"print(st)\n",
|
||||
"\n",
|
||||
"st.trim(stime,etime) # trim data stream to desired start and end time\n",
|
||||
"st.detrend('linear') # do a linear detrending\n",
|
||||
"st.detrend('demean') # subtract mean value\n",
|
||||
"tr = st[0] # extract first trace from stream (there is only one)\n",
|
||||
"tr.plot(); # plot trace"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Cell 2: Preparation of window\n",
|
||||
"wlen = 4. # window length in seconds\n",
|
||||
"npts = tr.stats.npts # number of samples\n",
|
||||
"dt = tr.stats.delta # sampling interval\n",
|
||||
"\n",
|
||||
"nwin = wlen/dt # number of samples in window\n",
|
||||
"nwin = int(pow(2, np.ceil(np.log(nwin)/np.log(2)))) # find next higher power of 2 of nwin\n",
|
||||
"nwmax = int(pow(2, np.ceil(np.log(npts)/np.log(2))))/8 # maximum window length (about 1/8 of length)\n",
|
||||
"nwin = min(nwin,nwmax) # limit nwin\n",
|
||||
"print(\"Samples in window: \",nwin)\n",
|
||||
"print(\"Total number of samples: \",npts)\n",
|
||||
"print(\"Sampling interval: \",dt)\n",
|
||||
"print(\"Length of trace [s]: \",npts*dt)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# The Hann window function\n",
|
||||
"def hann(nw):\n",
|
||||
" arg = 2.*np.pi*np.arange(0,nw)/nw # argument of cosine\n",
|
||||
" fwin = 0.5*(1.-np.cos(arg)) # Hann window\n",
|
||||
" return fwin\n",
|
||||
"\n",
|
||||
"# The boxcar window function\n",
|
||||
"def boxcar(nw):\n",
|
||||
" fwin = np.ones(nw)\n",
|
||||
" return fwin"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Plot the window function and its effect on the time series\n",
|
||||
"#\n",
|
||||
"fwin = hann(nwin) # Hann window\n",
|
||||
"plt.figure(figsize = [15,6])\n",
|
||||
"plt.subplot(221)\n",
|
||||
"plt.plot(fwin,'-k')\n",
|
||||
"plt.title(\"Hann window\")\n",
|
||||
"seg = tr.data[0:nwin]*fwin # multiply data with window\n",
|
||||
"plt.subplot(222)\n",
|
||||
"plt.plot(seg,'-k')\n",
|
||||
"fwin = boxcar(nwin) # Boxcar window\n",
|
||||
"plt.subplot(223)\n",
|
||||
"plt.plot(fwin,'-k')\n",
|
||||
"plt.title(\"Boxcar window\")\n",
|
||||
"seg = tr.data[0:nwin]*fwin # multiply data with window\n",
|
||||
"plt.subplot(224)\n",
|
||||
"plt.plot(seg,'-k')\n",
|
||||
"plt.show()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def movingWindowAnalysis(data,winfun,nwin,shift):\n",
|
||||
" \"\"\"\n",
|
||||
" Performs moving window analysis of a time series.\n",
|
||||
" data: data array\n",
|
||||
" winfun: name of the window function to be called\n",
|
||||
" nwin: number of window samples (power of 2)\n",
|
||||
" shift: displacement of moving window in samples\n",
|
||||
" \"\"\"\n",
|
||||
" fwin = winfun(nwin) # compute window values\n",
|
||||
" npts = len(data) # number of total samples\n",
|
||||
" nseg = int((npts-nwin)/shift)+1 # total number of expected data segment\n",
|
||||
" mwa = np.zeros((nwin//2+1,nseg)) # array for result (rfft returns N/2+1 samples) \n",
|
||||
" wa = 0 # start index of data segment\n",
|
||||
" we = nwin # end index of data segment\n",
|
||||
" jseg = 0 # initialize data segment counter\n",
|
||||
" while we < npts: # loop over segments\n",
|
||||
" seg = data[wa:we]*fwin # multiply data segment with window\n",
|
||||
" seg = seg-seg.mean() # subtract mean value of segment\n",
|
||||
" ftseg = np.abs(np.fft.rfft(seg)) # abs value of Fourier transform\n",
|
||||
" maxft = np.amax(ftseg) # max value of Fourier transform\n",
|
||||
" ftseg = ftseg/maxft+1.e-10 # normalize spectrum to its maximum, remove zeros\n",
|
||||
" mwa[:,jseg] = np.power(ftseg,0.25) # assign values to the matrix\n",
|
||||
" wa = wa+shift # move window start by shift\n",
|
||||
" we = we+shift # move window end by shift\n",
|
||||
" jseg = jseg+1 # increase segment counter\n",
|
||||
" return nseg,mwa # return number of segments and moving window matrix"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# carry out the moving window analysis\n",
|
||||
"tshift = 0.2*wlen\n",
|
||||
"shift = int(tshift/dt) # displacement of moving window in samples\n",
|
||||
"nseg,mwa = movingWindowAnalysis(tr.data,hann,nwin,shift) # compute spectrogram\n",
|
||||
"freq = np.fft.rfftfreq(nwin,dt) # Fourier frequencies\n",
|
||||
"print(\"Frequency range [Hz]: \",freq[0],freq[-1])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# plot the result\n",
|
||||
"fmaxplot = 250. # maximum frequency to be plotted\n",
|
||||
"nf = len(freq) # number of frequencies\n",
|
||||
"jfmax = int(fmaxplot/freq[-1]*nf) # max index for plotting \n",
|
||||
"extent = (0.5*wlen,0.5*wlen+nseg*tshift,fmaxplot,0.) # extent of matrix in true time and frequency\n",
|
||||
"plt.figure(figsize = [15,6])\n",
|
||||
"plt.imshow(mwa[0:jfmax,:],extent = extent,aspect=0.25) # do plotting\n",
|
||||
"plt.xlabel('Window center time [s]')\n",
|
||||
"plt.ylabel('Frequency [Hz]')\n",
|
||||
"plt.show()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Exercise \n",
|
||||
"\n",
|
||||
"1. Write a little routine for the Hamming window and do the analysis using this window\n",
|
||||
"2. Use the sqrt of the Fourier amplitude spectrum as output and compare\n",
|
||||
"3. Use other powers ($< 1$) of the Fourier amplitude (using np.power)\n",
|
||||
"4. Use the logarithm of the Fourier amplitude as output and compare (using np.log)\n",
|
||||
"5. Extend the frequency range when using the logarithm or a power of the Fourier amplitude\n",
|
||||
"6. Do a moving window analysis for the other available stations. Which ones appear good and which ones are bad?\n",
|
||||
"7. Try also records starting at 14:00\n",
|
||||
"\n",
|
||||
"Hint: when you change something in the code, use \"Run all below\" to see the changes."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### The Hamming window\n",
|
||||
"$ w(t) = 0.54 - 0.46 \\cos \\left( \\frac{2 \\pi t}{L} \\right) ~ , t \\in [0, L]$"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"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.6.7"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
90
05-Spectrogram/time_meets_frequency.ipynb
Normal file
90
05-Spectrogram/time_meets_frequency.ipynb
Normal file
@ -0,0 +1,90 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Dispersive Signals\n",
|
||||
"Up to now,we considered the spectral content of the entire time series. In many\n",
|
||||
"cases, the spectral content varies with time in a time series and one often wants to\n",
|
||||
"find out how this happens in detail. One important example are dispersed signals in\n",
|
||||
"which energy with different frequency content arrives at different times. Most\n",
|
||||
"prominent among these are seismic surface which exhibit significant dispersion if\n",
|
||||
"epicentral distances between seismic station and source are large. Quantifying the\n",
|
||||
"dispersion allows inferences on Earth structure. \n",
|
||||
"\n",
|
||||
"<img src=\"../images/seismogram.png\" alt=\"Drawing\" style=\"width: 700px;\"/>\n",
|
||||
"<img src=\"../images/stack-surface-waves.png\" alt=\"Drawing\" style=\"width: 700px;\"/>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## The Chirp or Sweep"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%matplotlib inline\n",
|
||||
"import matplotlib.pyplot as plt\n",
|
||||
"import numpy as np\n",
|
||||
"plt.rcParams['figure.figsize'] = 12, 8 # Slightly bigger plots by default"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from scipy.signal import chirp\n",
|
||||
"t = np.linspace(0, 30, 30001)\n",
|
||||
"w = chirp(t, f0=0.01, f1=3, t1=np.max(t), method='linear')\n",
|
||||
"plt.plot(t, w);"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Tasks\n",
|
||||
"1. What is special about the Chirp Signal?\n",
|
||||
"2. Plot the amplitude spectrum of the chirp signal, using numpys [FFT module](https://numpy.org/doc/stable/reference/routines.fft.html), including the correct frequncies on the x-axis!\n",
|
||||
"3. Think of a solution how to visualize the increasing frequency content using the Fourier tansform"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"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.6.7"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 2
|
||||
}
|
276
05-Spectrogram/uncertainty_instantaneous_frequency.ipynb
Normal file
276
05-Spectrogram/uncertainty_instantaneous_frequency.ipynb
Normal file
@ -0,0 +1,276 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# The Uncertainty Problem\n",
|
||||
"Using the moving window analysis, you may have noticed that we are not able to get a good resoltion in time and frequency. This is called the uncertainty problem, which you might have heard from in physics.\n",
|
||||
"\n",
|
||||
"Here, we a closer look how the uncertainty problem effects our sweep signal, when we apply the moving window average on this signal.\n",
|
||||
"\n",
|
||||
"### Tasks\n",
|
||||
"1. Read the documentation about [scipy's STFT](https://docs.scipy.org/doc/scipy/reference/generated/scipy.signal.chirp.html), especially the parameters nperseg, noverlap and nfft\n",
|
||||
"2. Play around with the parameters"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Preparation: load packages\n",
|
||||
"import os\n",
|
||||
"from obspy.core import read\n",
|
||||
"from obspy.core import UTCDateTime\n",
|
||||
"import numpy as np\n",
|
||||
"import matplotlib.pyplot as plt\n",
|
||||
"from scipy.signal import stft, chirp\n",
|
||||
"import pywt"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Load Chirp signal\n",
|
||||
"dt = 0.01\n",
|
||||
"t = np.arange(0, 5000) * dt\n",
|
||||
"data = chirp(t=t, f0=0.5, f1=25, t1=t[-1])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Applying Short-Time Fourier Transform (STFT) on data\n",
|
||||
"freqs, time, S = stft(x=data, fs=1/dt, window='hann', nperseg=256, noverlap=None, nfft=None)\n",
|
||||
"\n",
|
||||
"plt.figure(figsize = [15,6])\n",
|
||||
"plt.pcolormesh(time, freqs, np.abs(S))\n",
|
||||
"plt.xlabel(\"Time (s)\")\n",
|
||||
"plt.ylabel(\"Frequency (Hz)\");"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# The instantaneous Frequency\n",
|
||||
"The instantaneous frequency is a measure of the dominating frequency at each time instant of a signal and is determined as the time derivative of the instantaneous phase $\\phi (t)$. The phase can be obtained by constructing the analytical signal of an arbitrary time series:\n",
|
||||
"\n",
|
||||
"$$ \n",
|
||||
"\\hat{s}(t) = s(t) + i \\cdot \\mathcal{H} \\left( (s(t) \\right) = |\\hat{s}(t)| \\cdot \\exp (i \\phi (t)),\n",
|
||||
"$$\n",
|
||||
"\n",
|
||||
"where $\\mathcal{H}$ denotes the Hilbert-Transform. Note, the scipy function [hilbert()](https://docs.scipy.org/doc/scipy/reference/generated/scipy.signal.hilbert.html) computes the analytical signal. Now, we can derive the instantaneous phase as\n",
|
||||
"\n",
|
||||
"$$\n",
|
||||
"\\phi (t) = \\arg \\left( \\frac{Im(\\hat{s}(t))}{Re(\\hat{s}(t))} \\right).\n",
|
||||
"$$\n",
|
||||
"\n",
|
||||
"Note, the instantaneous phase is called wrapped phase if it is in the interval $(−\\pi, \\pi]$ or $[0, 2 \\pi)$. To determine the instantaneous frequncy, we need to [unwrap](https://numpy.org/doc/1.18/reference/generated/numpy.unwrap.html) the phase to get a continous function. Finally, the instantaneous frequency can be determined by\n",
|
||||
"\n",
|
||||
"$$\n",
|
||||
"f(t) = \\frac{1}{2 \\pi} \\frac{\\text{d} \\phi (t)}{\\text{d} t}\n",
|
||||
"$$\n",
|
||||
"\n",
|
||||
"1. Create a time series with \n",
|
||||
" 1. increasing / decreasing frequncy content, e.g. a [Chirp-Signal](https://docs.scipy.org/doc/scipy/reference/generated/scipy.signal.chirp.html)\n",
|
||||
" 2. increasing and decreasing frequency content, i.e. overlapping of two chirp signals (how does the spectrogram looks like?)\n",
|
||||
"3. Write a function that computes the instantaneous frequency (Hint: use np.diff for instantaneous phase derivative, np.unwrap to unwrap the phase.)\n",
|
||||
"4. What is the meaning of $|\\hat{s}(t)|$? (Hint: plot it!) \n",
|
||||
"5. Plot the results of the instantaneous frequnecy"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Define the signal\n",
|
||||
"dt = 0.01\n",
|
||||
"t = np.arange(0, 5000) * dt\n",
|
||||
"data = chirp(t=t, f0=0.5, f1=25, t1=t[-1])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Function for instantaneous frequency\n",
|
||||
"from scipy.signal import hilbert\n",
|
||||
"def istantaneous_frequency(x, dt):\n",
|
||||
" # To continue"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Plot results"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# The Continuous Wavelet Transform (CWT)\n",
|
||||
"\n",
|
||||
"We learned during the STFT about the uncertainty problem. Using the CWT results in a different time-frequency resolution. For high frequency we get poor frequency resolution by good time resoltution, whereas for low frequencies we get poor time resolution bur good frequency resolution. As you can see, there is no recipe when to use STFT, instantaneous frequnecy or CWT, but it is good to know where are the advantages and disadvantages.\n",
|
||||
"\n",
|
||||
"The CWT is defined as\n",
|
||||
"\n",
|
||||
"$$\n",
|
||||
"\\text{CWT}_{a, b} \\left(s(t) \\right) = \\frac{1}{\\sqrt{|a|}} ~ \\int_{-\\infty}^{\\infty} s(t) ~ \\Psi^\\ast \\left( \\frac{t-b}{a} \\right) \\text{d} t ~,\n",
|
||||
"$$\n",
|
||||
"\n",
|
||||
"where $a$ denotes the scaling factor of the mother wavelet $\\Psi(t)$ and $b$ denotes the translation. \n",
|
||||
"In other words, the CWT is a convolution of the signal $s(t)$ with a conjugate complex and scaled wavelet-function ([here](https://en.wikipedia.org/wiki/Continuous_wavelet_transform) you find a good visualisation). Due to the different scaled wavelets, the CWT reacts well on abruptly changes in the signal.\n",
|
||||
"Usally we get instead of the frequency on the y-axis the scaling factor $a$, but it depends on the center frequency $f_c$ of the scaled wavelet by\n",
|
||||
"\n",
|
||||
"$$\n",
|
||||
"f_a = \\frac{f_c}{a} .\n",
|
||||
"$$\n",
|
||||
"\n",
|
||||
"Luckily it exists a library [pywt](https://pywavelets.readthedocs.io/en/latest/) to compute the CWT with several wavelets.\n",
|
||||
"For the following example we use a [complex Morlet wavelet](https://en.wikipedia.org/wiki/Morlet_wavelet) per default.\n",
|
||||
"\n",
|
||||
"#### Tasks\n",
|
||||
"\n",
|
||||
"1. Try to plot a time-frequency representation of different signals and compare with results of STFT\n",
|
||||
"2. Use different [mother wavelets](https://pywavelets.readthedocs.io/en/latest/ref/wavelets.html)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Display a mother wavelet\n",
|
||||
"wav, a = wavelet = pywt.ContinuousWavelet(\"cmor2-0.95\").wavefun();\n",
|
||||
"plt.plot(a, wav.real, label=\"real part\");\n",
|
||||
"plt.plot(a, wav.imag, label=\"imag part\");\n",
|
||||
"plt.legend();"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def frequency2scale(fmin, fmax, waveletname, dt):\n",
|
||||
" \"\"\"\n",
|
||||
" Computes scales for given frequency values fmin and famx.\n",
|
||||
" :param fmin: frequency minimum\n",
|
||||
" :param fmax: frequency maximum\n",
|
||||
" :param waveletname: name of wavelet\n",
|
||||
" :param dt: sampling interval (s)\n",
|
||||
" \"\"\"\n",
|
||||
"\n",
|
||||
" # Determine lower bound of scales\n",
|
||||
" scale_start = 0.1\n",
|
||||
" scale_min = pywt.scale2frequency(waveletname, scale_start)\n",
|
||||
" while scale_min/dt > fmax:\n",
|
||||
" scale_start *= 1.1\n",
|
||||
" scale_min = pywt.scale2frequency(waveletname, scale_start)\n",
|
||||
" scale_min = round(scale_start, 2)\n",
|
||||
"\n",
|
||||
" # Determine upper bound of scales\n",
|
||||
" scale_start = 1\n",
|
||||
" scale_max = pywt.scale2frequency(waveletname, scale_start)\n",
|
||||
" while scale_max/dt > fmin:\n",
|
||||
" scale_start *= 1.1\n",
|
||||
" scale_max = pywt.scale2frequency(waveletname, scale_start)\n",
|
||||
" scale_max = round(scale_start, 2)\n",
|
||||
"\n",
|
||||
" return scale_min, scale_max\n",
|
||||
"\n",
|
||||
"def scaleogram(x, fs, waveletname=\"cmor2-0.95\", fmin=1, fmax=25, num=100):\n",
|
||||
" \"\"\"\n",
|
||||
" Computes CWT of an signal x with sampling rate fs in Hz \n",
|
||||
" :param x: time seris\n",
|
||||
" :param fs: sampling rate\n",
|
||||
" :param waveletname: name of wavelet\n",
|
||||
" :param fmin: minimum frequency\n",
|
||||
" :param fmax: maximum frequency\n",
|
||||
" :param num: length of scale array\n",
|
||||
" \"\"\"\n",
|
||||
"\n",
|
||||
" # Check all input parameters and change them is they do not fit\n",
|
||||
" dt = 1 / fs\n",
|
||||
" f_ny = 1 / (2 * dt) # Nyquist frequency\n",
|
||||
" if f_ny < fmax:\n",
|
||||
" fmax = f_ny\n",
|
||||
"\n",
|
||||
" if fmin >= fmax:\n",
|
||||
" msg = \"Min. frequency {} is greater than max. frequency {} to compute scaleograms.\".format(fmin, fmax)\n",
|
||||
" raise ValueError(msg)\n",
|
||||
"\n",
|
||||
" # Determine scales from fmin and fmax\n",
|
||||
" scale_min, scale_max = frequency2scale(fmin, fmax, waveletname=waveletname, dt=dt)\n",
|
||||
" scales = np.logspace(np.log10(scale_min), np.log10(scale_max), num=num)\n",
|
||||
"\n",
|
||||
" # Generate scaleogram\n",
|
||||
" coefficients, frequencies = pywt.cwt(x, scales, waveletname, sampling_period=dt)\n",
|
||||
" coefficients = np.abs(coefficients)**2\n",
|
||||
" time = np.linspace(0, dt * len(x), num=len(x))\n",
|
||||
" \n",
|
||||
" return frequencies, time, coefficients"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"f_cwt, t_cwt, coeff = scaleogram(data, fs=1/(t[1]-t[0]))\n",
|
||||
"plt.figure(figsize=(16, 8))\n",
|
||||
"plt.pcolormesh(t_cwt, f_cwt, np.abs(coeff))\n",
|
||||
"plt.xlabel(\"Time (s)\")\n",
|
||||
"plt.xlabel(\"Frequency (Hz)\");"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"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.6.7"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 2
|
||||
}
|
35
06-Surface_Waves/movingWindow.py
Normal file
35
06-Surface_Waves/movingWindow.py
Normal file
@ -0,0 +1,35 @@
|
||||
import numpy as np
|
||||
|
||||
def movingWindowAnalysis(data,winfun,nwin,shift,exp):
|
||||
"""
|
||||
Performs moving window analysis of a time series.
|
||||
data: data array
|
||||
winfun: name of the window function to be called
|
||||
nwin: number of window samples (power of 2)
|
||||
shift: displacement of moving window in samples
|
||||
exp: exponent for taking power of spectrum
|
||||
"""
|
||||
fwin = winfun(nwin) # compute window values
|
||||
npts = len(data) # number of total samples
|
||||
nseg = int((npts-nwin)/shift)+1 # total number of expected data segment
|
||||
mwa = np.zeros((nwin//2+1,nseg)) # array for result (rfft returns N/2+1 samples)
|
||||
wa = 0 # start index of data segment
|
||||
we = nwin # end index of data segment
|
||||
jseg = 0 # initialize data segment counter
|
||||
while we < npts: # loop over segments
|
||||
seg = data[wa:we]*fwin # multiply data segment with window
|
||||
seg = seg-seg.mean() # subtract mean value of segment
|
||||
ftseg = np.abs(np.fft.rfft(seg)) # abs value of Fourier transform
|
||||
maxft = np.amax(ftseg) # max value of Fourier transform
|
||||
ftseg = ftseg/maxft+1.e-10 # normalize spectrum to its maximum, remove zeros
|
||||
mwa[:,jseg] = np.power(ftseg,exp) # assign values to the matrix
|
||||
wa = wa+shift # move window start by shift
|
||||
we = we+shift # move window end by shift
|
||||
jseg = jseg+1 # increase segment counter
|
||||
return nseg,mwa # return number of segments and moving window matrix
|
||||
#------------------------------------------------------------------------------------
|
||||
#
|
||||
def hann(nw):
|
||||
arg = 2.*np.pi*np.arange(0,nw)/nw # argument of cosine
|
||||
fwin = 0.5*(1.-np.cos(arg)) # Hann window
|
||||
return fwin
|
47
06-Surface_Waves/multipleFilter.py
Normal file
47
06-Surface_Waves/multipleFilter.py
Normal file
@ -0,0 +1,47 @@
|
||||
import numpy as np
|
||||
|
||||
def multipleFilterAnalysis(data,alfa,cfreq,dt,ndec):
|
||||
"""
|
||||
Perform a multiple filter analysis of data.
|
||||
data: Array of detrended and demeaned data whose length is power of 2
|
||||
alfa: Width parameter of Gaussian bandpass filter
|
||||
cfreq: Array of center frequencies of Gaussian filter
|
||||
dt: sampling interval of data
|
||||
ndec: decimation factor for instantaneous amplitude output
|
||||
"""
|
||||
npts = len(data)
|
||||
nd = int(pow(2, np.ceil(np.log(npts)/np.log(2)))) # find next higher power of 2 of npts
|
||||
ftd = np.fft.rfft(data,nd) # Fourier transform of entire data set (pos. freq.)
|
||||
# data are padded with zeros since npts <= nd
|
||||
freq = np.fft.rfftfreq(nd,dt) # Fourier frequencies (positive frequencies)
|
||||
nt = int(np.ceil(npts/ndec))
|
||||
mfa = np.zeros((len(cfreq),nt)) # numpy array for MFA result
|
||||
for jf,cf in enumerate(cfreq):
|
||||
hg = np.exp(-alfa*((freq-cf)/cf)**2) # Gaussian filter (use f instead of omega here)
|
||||
fk = hg*ftd # multiply FT of data with filter
|
||||
qk = np.complex(0,1)*fk # FT of Hilbert transform
|
||||
ftk = np.fft.irfft(fk) # filtered data
|
||||
qtk = np.fft.irfft(qk) # Hilbert transform of filtered data
|
||||
at = np.sqrt(ftk**2+qtk**2) # instantaneous amplitude
|
||||
mfa[jf,:] = at[0:npts:ndec] # store decimated original result
|
||||
return mfa
|
||||
#-----------------------------------------------------------------------
|
||||
# normalize multiple filter result either along time or frequency axis
|
||||
def normalizeMFT(mfa,mode,exp):
|
||||
"""
|
||||
Normalize the result of the mutiple filtering operation.
|
||||
mfa: array with instantaneous amplitudes versus frequency (mfa(f,t))
|
||||
mode: normalization mode: if 'time', normalize along time axis
|
||||
else normalize along frequency axis
|
||||
exp: exponent for modifying inst amp using a power less than 1
|
||||
"""
|
||||
nf,nt = mfa.shape
|
||||
if mode == 'time':
|
||||
for jf in range(0,nf):
|
||||
mfamax = np.amax(mfa[jf,:])
|
||||
mfa[jf,:] = np.power(mfa[jf,:]/mfamax+1.e-10,exp)
|
||||
else:
|
||||
for jt in range(0,nt):
|
||||
mfamax = np.amax(mfa[:,jt])
|
||||
mfa[:,jt] = np.power(mfa[:,jt]/mfamax+1.e-10,exp)
|
||||
return mfa
|
268
06-Surface_Waves/phaseVelocityCrossCorr.ipynb
Normal file
268
06-Surface_Waves/phaseVelocityCrossCorr.ipynb
Normal file
@ -0,0 +1,268 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Two-station method\n",
|
||||
"Here we compute the phase velocity form seismograms of two stations lying on a common great circle. We make use of the fact that the phase of the Fourier tranform of the cross-correlation of the two seismograms is equal to the phase difference between the surface wave trains from which we calclate the phase velocity."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"jupyter": {
|
||||
"outputs_hidden": false
|
||||
}
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Preparation: load packages, set some basic options \n",
|
||||
"%matplotlib inline\n",
|
||||
"from obspy import UTCDateTime\n",
|
||||
"from obspy.clients.fdsn import Client\n",
|
||||
"from obspy.geodetics import locations2degrees\n",
|
||||
"import numpy as np\n",
|
||||
"import matplotlib.pylab as plt\n",
|
||||
"from multipleFilter import multipleFilterAnalysis, normalizeMFT \n",
|
||||
"plt.style.use('ggplot')\n",
|
||||
"plt.rcParams['figure.figsize'] = 15, 4\n",
|
||||
"plt.rcParams['lines.linewidth'] = 0.5"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"jupyter": {
|
||||
"outputs_hidden": false
|
||||
}
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Get data from earthquake (Gulf of Alaska, m=7.9, d = 24 km)\n",
|
||||
"client = Client(\"BGR\")\n",
|
||||
"t1 = UTCDateTime(\"2018-01-23T09:31:42.000\")\n",
|
||||
"st1raw = client.get_waveforms(\"GR\",\"TNS\",\"\",\"LHZ\",t1,t1 + 2 * 3600, # data of station TNS of GRSN\n",
|
||||
" attach_response = True)\n",
|
||||
"inv1 = client.get_stations(network=\"GR\",station=\"TNS\",\n",
|
||||
" channel='LHZ',level=\"channel\",starttime=t1) # inventory info about TNS\n",
|
||||
"client = Client(\"GFZ\")\n",
|
||||
"st2raw = client.get_waveforms(\"GE\",\"STU\",\"\",\"LHZ\",t1,t1 + 2 * 3600, # data of station STU of GEOFON\n",
|
||||
" attach_response = True)\n",
|
||||
"inv2 = client.get_stations(network=\"GE\",station=\"STU\",\n",
|
||||
" channel='LHZ',level=\"channel\",starttime=t1) # inventory info about STU"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"jupyter": {
|
||||
"outputs_hidden": false
|
||||
}
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"st1 = st1raw.copy() # copy data\n",
|
||||
"st2 = st2raw.copy()\n",
|
||||
"st1.detrend('linear') # detrend\n",
|
||||
"st2.detrend('linear')\n",
|
||||
"st1.remove_response(output='VEL',zero_mean = True, # remove response (pre-filter is important)\n",
|
||||
" pre_filt = (0.001, 0.005, 0.4, 0.5))\n",
|
||||
"st2.remove_response(output='VEL',zero_mean = True, # remove response(pre-filter is important)\n",
|
||||
" pre_filt = (0.001, 0.005, 0.4, 0.5))\n",
|
||||
"st1.plot() # plot data\n",
|
||||
"st2.plot()\n",
|
||||
"print(st1)\n",
|
||||
"print(st2)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"jupyter": {
|
||||
"outputs_hidden": false
|
||||
}
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"co = inv1.get_coordinates(\"GR.TNS..LHZ\") # extract coordinates from inventory\n",
|
||||
"tns = (co[\"longitude\"],co[\"latitude\"])\n",
|
||||
"co = inv2.get_coordinates(\"GE.STU..LHZ\")\n",
|
||||
"stu = (co[\"longitude\"],co[\"latitude\"])\n",
|
||||
"event = (-149.09,56.05) # coordinates of event\n",
|
||||
"dis = locations2degrees(tns[1],tns[0],stu[1],stu[0])*np.pi/180.*6371. # epicentral distance\n",
|
||||
"print(\"Distance in km: \",dis)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"<img src=\"../images/phase_velocity_map.png\">"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"jupyter": {
|
||||
"outputs_hidden": false
|
||||
}
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"tw1 = UTCDateTime(\"2018-01-23T10:05:00.000\") # cut seismograms to surface wave train\n",
|
||||
"tw2 = UTCDateTime(\"2018-01-23T10:16:00.000\")\n",
|
||||
"st1.trim(tw1,tw2)\n",
|
||||
"st2.trim(tw1,tw2)\n",
|
||||
"st1.plot()\n",
|
||||
"st2.plot()\n",
|
||||
"npts = st1[0].stats.npts\n",
|
||||
"dt = st1[0].stats.delta\n",
|
||||
"print(\"Number of samples: \",npts,\"Sampling interval: \",dt)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"jupyter": {
|
||||
"outputs_hidden": false
|
||||
}
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"cc = np.correlate(st2[0],st1[0],mode=\"full\") # Cross correlation: cc(k)=sum_n a(n+k)b(n)\n",
|
||||
"tm = np.arange(-(npts-1),npts)*dt # includes negative lags as well\n",
|
||||
"plt.plot(tm,cc,'-k')\n",
|
||||
"plt.show()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"jupyter": {
|
||||
"outputs_hidden": false
|
||||
},
|
||||
"scrolled": true
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"ns = 256 # limit length of cross correlation to first ns points\n",
|
||||
"ccp = cc[npts-1:npts-1+ns] # cross correlation for positive lag\n",
|
||||
"print(len(ccp))\n",
|
||||
"tm = np.arange(0,ns)*dt # associated time values\n",
|
||||
"plt.plot(tm,ccp,'-k')\n",
|
||||
"plt.xlabel(\"Time\")\n",
|
||||
"plt.show()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"jupyter": {
|
||||
"outputs_hidden": false
|
||||
}
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"nd = int(pow(2, np.ceil(np.log(ns)/np.log(2)))) # Fourier transfrom of the cross correlation\n",
|
||||
"fcp = np.fft.rfft(ccp,nd)\n",
|
||||
"freq = np.fft.rfftfreq(nd,dt)\n",
|
||||
"print(nd,dt,len(freq),freq[0:4])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"jupyter": {
|
||||
"outputs_hidden": false
|
||||
}
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"fmax = 0.1 # compute phase up to maximum frequency fmax\n",
|
||||
"fny = 1./(2.*dt)\n",
|
||||
"jmax = int(fmax/fny*(nd//2-1))\n",
|
||||
"print(jmax)\n",
|
||||
"spec = fcp[0:jmax]\n",
|
||||
"frs = freq[0:jmax]\n",
|
||||
"phase = np.unwrap(np.angle(spec)) # unwrap the phase owing to 2*pi multiples"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"jupyter": {
|
||||
"outputs_hidden": false
|
||||
}
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"plt.subplot(311)\n",
|
||||
"plt.plot(frs,np.abs(spec),\"-k\") # plot abs of spectrum\n",
|
||||
"plt.subplot(312)\n",
|
||||
"plt.plot(frs,np.angle(spec),\"-k\") # plot phase of spectrum\n",
|
||||
"plt.subplot(313)\n",
|
||||
"plt.plot(frs,phase,\"-k\") # plot unwrapped phase\n",
|
||||
"plt.xlabel(\"Frequency [Hz]\")\n",
|
||||
"plt.show()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"jupyter": {
|
||||
"outputs_hidden": false
|
||||
}
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"j1 = int(jmax*0.1)\n",
|
||||
"j2 = int(jmax*0.6)\n",
|
||||
"phavel = -2.*np.pi*frs[j1:j2]*dis/phase[j1:j2] # calculate phase velocity and plot\n",
|
||||
"plt.plot(frs[j1:j2],phavel,'-k')\n",
|
||||
"plt.xlabel(\"Frequency [Hz]\")\n",
|
||||
"plt.ylabel(\"Phase velocity [km/s]\")\n",
|
||||
"plt.show()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"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.6.7"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 4
|
||||
}
|
256
06-Surface_Waves/surfaceWaveGroupVelocity.ipynb
Normal file
256
06-Surface_Waves/surfaceWaveGroupVelocity.ipynb
Normal file
@ -0,0 +1,256 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Surface Wave Velocity (Single Station Approach)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"jupyter": {
|
||||
"outputs_hidden": false
|
||||
}
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Preparation: load packages, set some basic options \n",
|
||||
"%matplotlib inline\n",
|
||||
"from obspy import UTCDateTime\n",
|
||||
"from obspy.clients.fdsn import Client\n",
|
||||
"from obspy.clients.fdsn import URL_MAPPINGS\n",
|
||||
"import numpy as np\n",
|
||||
"import matplotlib.pylab as plt\n",
|
||||
"from movingWindow import movingWindowAnalysis, hann # import the moving window analysis routines\n",
|
||||
"from multipleFilter import multipleFilterAnalysis, normalizeMFT # import the multiple filter analysis routines\n",
|
||||
"plt.style.use('ggplot')\n",
|
||||
"plt.rcParams['figure.figsize'] = 15, 4\n",
|
||||
"plt.rcParams['lines.linewidth'] = 0.5"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Data from data archive using obspy webfdsn client\n",
|
||||
"FDSN stands for the International Federal of Digital Seismic Networks (www.fdsn.org). Citation from their web site: \"The International Federation of Digital Seismograph Networks (FDSN) is a global organization. Its membership is comprised of groups responsible for the installation and maintenance of seismographs either within their geographic borders or globally. Membership in the FDSN is open to all organizations that operate more than one broadband station. Members agree to coordinate station siting and provide free and open access to their data. This cooperation helps scientists all over the world to further the advancement of earth science and particularly the study of global seismic activity.\"\n",
|
||||
"BGR (Bundesanstalt für Geowissenschaften und Rohstoffe) operates broadband stations in Germany and operates a data archive. It is member of the FDSN. That is why we can freely download data from them.\n",
|
||||
"There are other data archives around the world which are also member of the FDSN and allow opn access to their data (see OBSPY documentation "
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"jupyter": {
|
||||
"outputs_hidden": false
|
||||
}
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"for key in sorted(URL_MAPPINGS.keys()): # eine Liste der Archive\n",
|
||||
" print(\"{0:<7} {1}\".format(key, URL_MAPPINGS[key]))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Get waveforms for the Tohoku earthquake for station TNS of the German Regional Seismic Network."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"jupyter": {
|
||||
"outputs_hidden": false
|
||||
}
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"client = Client(\"BGR\") # data archive from which data are fetched\n",
|
||||
"t1 = UTCDateTime(\"2018-01-14T09:19:00.000\") # desired start time of data\n",
|
||||
"stc = client.get_waveforms(\"GR\", \"TNS\", \"\", \"LHZ\", t1, # use fdsn web client to download data\n",
|
||||
" t1 + 7200., attach_response=True)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"jupyter": {
|
||||
"outputs_hidden": false
|
||||
}
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"stc.detrend('linear') # take out linear trend\n",
|
||||
"stc.plot()\n",
|
||||
"print(stc)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"jupyter": {
|
||||
"outputs_hidden": false
|
||||
}
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"st = stc.copy() # proceed with copy to avoid repeated downloading\n",
|
||||
"ta = UTCDateTime(\"2018-01-14T10:05:00.000000Z\") # cut to surface wave train\n",
|
||||
"te = UTCDateTime(\"2018-01-14T10:22:00.000000Z\")\n",
|
||||
"st.trim(ta,te)\n",
|
||||
"st.taper(0.05,\"hann\") # apply a taper to bring signal to zero at ends\n",
|
||||
"st.plot() # dispersion is now well visible\n",
|
||||
"print(st)\n",
|
||||
"tr = st[0]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Moving window approach"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"jupyter": {
|
||||
"outputs_hidden": false
|
||||
}
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"wlen = 256.0 # window length in sec\n",
|
||||
"npts = tr.stats.npts # number of samples in the trace\n",
|
||||
"dt = tr.stats.delta # sample interval\n",
|
||||
"nwin = int(wlen/dt) # nuber of samples in window\n",
|
||||
"nwin = int(pow(2, np.ceil(np.log(nwin)/np.log(2)))) # find next higher power of 2 of nwin\n",
|
||||
"shift = nwin//8\n",
|
||||
"tshift = shift*dt\n",
|
||||
"nseg,mwa = movingWindowAnalysis(tr.data,hann,nwin,shift,2.0) # compute spectrogram, exponent=2.0\n",
|
||||
"freq = np.fft.rfftfreq(nwin,dt) # Fourier frequencies\n",
|
||||
"print(\"Frequency range [Hz]: \",freq[0],freq[-1])\n",
|
||||
"print(\"Number of frequency samples: \",len(freq))\n",
|
||||
"print(\"Window length in samples: \",nwin)\n",
|
||||
"print(\"Window shift in samples: \",shift)\n",
|
||||
"print(\"Number of segments: \",nseg)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"jupyter": {
|
||||
"outputs_hidden": false
|
||||
}
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# plot the result\n",
|
||||
"st.plot() # plot seismogram again\n",
|
||||
"fmax = 0.08 # maximum frequency to be plotted\n",
|
||||
"fmin = 0.02\n",
|
||||
"nf = len(freq)-1 # number of frequencies\n",
|
||||
"jfmax = int(fmax/freq[-1]*nf) # max index for plotting \n",
|
||||
"jfmin = int(fmin/freq[-1]*nf) # min index for plotting \n",
|
||||
"extent = (0.5*wlen,0.5*wlen+nseg*tshift,fmin,fmax) # extent of matrix in true time and frequency\n",
|
||||
"asp = 0.5*nseg*tshift/(fmax-fmin)\n",
|
||||
"fg = plt.figure(figsize = [12.5,12])\n",
|
||||
"plt.imshow(mwa[jfmin:jfmax,:],extent = extent, aspect = asp, \n",
|
||||
" origin = \"lower\", interpolation = \"bicubic\") # plotting with interpolation\n",
|
||||
"plt.xlabel('Window center time [s]')\n",
|
||||
"plt.ylabel('Frequency [Hz]')\n",
|
||||
"plt.show()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Observe the change of frequency content with time from 0.03 Hz initially to 0.06 Hz towards the end of the record. Given a epicentral distance $d$, we could transform the time axis into a group velocity axis via $U=d/t$."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Multiple filtering approach"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"jupyter": {
|
||||
"outputs_hidden": false
|
||||
}
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"alfa = 50.0 # filter width parameter\n",
|
||||
"cfreq = np.linspace(fmin,fmax,100) # array of filter center frequencies\n",
|
||||
"mfa = multipleFilterAnalysis(tr.data,alfa,cfreq,dt,1)\n",
|
||||
"mfa = normalizeMFT(mfa,'freq',2.0)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"jupyter": {
|
||||
"outputs_hidden": false
|
||||
}
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"st.plot()\n",
|
||||
"fg = plt.figure(figsize = [12.5,12])\n",
|
||||
"extent = (0.,npts*dt,fmin,fmax) # extent of matrix in true time and frequency\n",
|
||||
"asp = 0.5*npts*dt/(fmax-fmin)\n",
|
||||
"plt.imshow(mfa,extent = extent, aspect = asp, \n",
|
||||
" origin = \"lower\", interpolation = \"bicubic\") # plotting\n",
|
||||
"plt.xlabel('Time [s]')\n",
|
||||
"plt.ylabel('Frequency [Hz]')\n",
|
||||
"plt.show()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"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.6.7"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 4
|
||||
}
|
BIN
07-Receiver_Functions/data/PB01.BHE.20110225.sac
Normal file
BIN
07-Receiver_Functions/data/PB01.BHE.20110225.sac
Normal file
Binary file not shown.
BIN
07-Receiver_Functions/data/PB01.BHE.20110306.sac
Normal file
BIN
07-Receiver_Functions/data/PB01.BHE.20110306.sac
Normal file
Binary file not shown.
BIN
07-Receiver_Functions/data/PB01.BHE.20110513.sac
Normal file
BIN
07-Receiver_Functions/data/PB01.BHE.20110513.sac
Normal file
Binary file not shown.
BIN
07-Receiver_Functions/data/PB01.BHN.20110225.sac
Normal file
BIN
07-Receiver_Functions/data/PB01.BHN.20110225.sac
Normal file
Binary file not shown.
BIN
07-Receiver_Functions/data/PB01.BHN.20110306.sac
Normal file
BIN
07-Receiver_Functions/data/PB01.BHN.20110306.sac
Normal file
Binary file not shown.
BIN
07-Receiver_Functions/data/PB01.BHN.20110513.sac
Normal file
BIN
07-Receiver_Functions/data/PB01.BHN.20110513.sac
Normal file
Binary file not shown.
BIN
07-Receiver_Functions/data/PB01.BHZ.20110225.sac
Normal file
BIN
07-Receiver_Functions/data/PB01.BHZ.20110225.sac
Normal file
Binary file not shown.
BIN
07-Receiver_Functions/data/PB01.BHZ.20110306.sac
Normal file
BIN
07-Receiver_Functions/data/PB01.BHZ.20110306.sac
Normal file
Binary file not shown.
BIN
07-Receiver_Functions/data/PB01.BHZ.20110513.sac
Normal file
BIN
07-Receiver_Functions/data/PB01.BHZ.20110513.sac
Normal file
Binary file not shown.
473
07-Receiver_Functions/example_events.xml
Normal file
473
07-Receiver_Functions/example_events.xml
Normal file
@ -0,0 +1,473 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<ns0:quakeml xmlns:ns0="http://quakeml.org/xmlns/quakeml/1.2" xmlns="http://quakeml.org/xmlns/bed/1.2">
|
||||
<eventParameters publicID="smi:service.iris.edu/fdsnws/event/1/query">
|
||||
<event publicID="smi:service.iris.edu/fdsnws/event/1/query?eventid=3287729">
|
||||
<preferredOriginID>smi:service.iris.edu/fdsnws/event/1/query?originid=10171447</preferredOriginID>
|
||||
<preferredMagnitudeID>smi:service.iris.edu/fdsnws/event/1/query?magnitudeid=16917168</preferredMagnitudeID>
|
||||
<type>earthquake</type>
|
||||
<description>
|
||||
<text>CENTRAL MID-ATLANTIC RIDGE</text>
|
||||
<type>Flinn-Engdahl region</type>
|
||||
</description>
|
||||
<origin publicID="smi:service.iris.edu/fdsnws/event/1/query?originid=10171447">
|
||||
<time>
|
||||
<value>2011-05-15T13:08:15.420000Z</value>
|
||||
</time>
|
||||
<latitude>
|
||||
<value>0.4584</value>
|
||||
</latitude>
|
||||
<longitude>
|
||||
<value>-25.6088</value>
|
||||
</longitude>
|
||||
<depth>
|
||||
<value>18900.0</value>
|
||||
</depth>
|
||||
<creationInfo>
|
||||
<author>ISC</author>
|
||||
</creationInfo>
|
||||
</origin>
|
||||
<magnitude publicID="smi:service.iris.edu/fdsnws/event/1/query?magnitudeid=16917168">
|
||||
<mag>
|
||||
<value>6.1</value>
|
||||
</mag>
|
||||
<type>MW</type>
|
||||
<originID>smi:service.iris.edu/fdsnws/event/1/query?originid=10171449</originID>
|
||||
<creationInfo>
|
||||
<author>GCMT</author>
|
||||
</creationInfo>
|
||||
</magnitude>
|
||||
</event>
|
||||
<event publicID="smi:service.iris.edu/fdsnws/event/1/query?eventid=3287620">
|
||||
<preferredOriginID>smi:service.iris.edu/fdsnws/event/1/query?originid=10167818</preferredOriginID>
|
||||
<preferredMagnitudeID>smi:service.iris.edu/fdsnws/event/1/query?magnitudeid=16912325</preferredMagnitudeID>
|
||||
<type>earthquake</type>
|
||||
<description>
|
||||
<text>COSTA RICA</text>
|
||||
<type>Flinn-Engdahl region</type>
|
||||
</description>
|
||||
<origin publicID="smi:service.iris.edu/fdsnws/event/1/query?originid=10167818">
|
||||
<time>
|
||||
<value>2011-05-13T22:47:55.340000Z</value>
|
||||
</time>
|
||||
<latitude>
|
||||
<value>10.1114</value>
|
||||
</latitude>
|
||||
<longitude>
|
||||
<value>-84.1889</value>
|
||||
</longitude>
|
||||
<depth>
|
||||
<value>76800.0</value>
|
||||
</depth>
|
||||
<creationInfo>
|
||||
<author>ISC</author>
|
||||
</creationInfo>
|
||||
</origin>
|
||||
<magnitude publicID="smi:service.iris.edu/fdsnws/event/1/query?magnitudeid=16912325">
|
||||
<mag>
|
||||
<value>6.0</value>
|
||||
</mag>
|
||||
<type>MW</type>
|
||||
<originID>smi:service.iris.edu/fdsnws/event/1/query?originid=10167822</originID>
|
||||
<creationInfo>
|
||||
<author>GCMT</author>
|
||||
</creationInfo>
|
||||
</magnitude>
|
||||
</event>
|
||||
<event publicID="smi:service.iris.edu/fdsnws/event/1/query?eventid=3285786">
|
||||
<preferredOriginID>smi:service.iris.edu/fdsnws/event/1/query?originid=10137185</preferredOriginID>
|
||||
<preferredMagnitudeID>smi:service.iris.edu/fdsnws/event/1/query?magnitudeid=16870909</preferredMagnitudeID>
|
||||
<type>earthquake</type>
|
||||
<description>
|
||||
<text>SOUTH OF PANAMA</text>
|
||||
<type>Flinn-Engdahl region</type>
|
||||
</description>
|
||||
<origin publicID="smi:service.iris.edu/fdsnws/event/1/query?originid=10137185">
|
||||
<time>
|
||||
<value>2011-04-30T08:19:16.720000Z</value>
|
||||
</time>
|
||||
<latitude>
|
||||
<value>6.8511</value>
|
||||
</latitude>
|
||||
<longitude>
|
||||
<value>-82.3594</value>
|
||||
</longitude>
|
||||
<depth>
|
||||
<value>10000.0</value>
|
||||
</depth>
|
||||
<creationInfo>
|
||||
<author>ISC</author>
|
||||
</creationInfo>
|
||||
</origin>
|
||||
<magnitude publicID="smi:service.iris.edu/fdsnws/event/1/query?magnitudeid=16870909">
|
||||
<mag>
|
||||
<value>6.2</value>
|
||||
</mag>
|
||||
<type>MW</type>
|
||||
<originID>smi:service.iris.edu/fdsnws/event/1/query?originid=10137190</originID>
|
||||
<creationInfo>
|
||||
<author>GCMT</author>
|
||||
</creationInfo>
|
||||
</magnitude>
|
||||
</event>
|
||||
<event publicID="smi:service.iris.edu/fdsnws/event/1/query?eventid=3284483">
|
||||
<preferredOriginID>smi:service.iris.edu/fdsnws/event/1/query?originid=10109851</preferredOriginID>
|
||||
<preferredMagnitudeID>smi:service.iris.edu/fdsnws/event/1/query?magnitudeid=16833821</preferredMagnitudeID>
|
||||
<type>earthquake</type>
|
||||
<description>
|
||||
<text>SOUTH OF KERMADEC ISLANDS</text>
|
||||
<type>Flinn-Engdahl region</type>
|
||||
</description>
|
||||
<origin publicID="smi:service.iris.edu/fdsnws/event/1/query?originid=10109851">
|
||||
<time>
|
||||
<value>2011-04-18T13:03:04.360000Z</value>
|
||||
</time>
|
||||
<latitude>
|
||||
<value>-34.286</value>
|
||||
</latitude>
|
||||
<longitude>
|
||||
<value>179.9433</value>
|
||||
</longitude>
|
||||
<depth>
|
||||
<value>98100.0</value>
|
||||
</depth>
|
||||
<creationInfo>
|
||||
<author>ISC</author>
|
||||
</creationInfo>
|
||||
</origin>
|
||||
<magnitude publicID="smi:service.iris.edu/fdsnws/event/1/query?magnitudeid=16833821">
|
||||
<mag>
|
||||
<value>6.5</value>
|
||||
</mag>
|
||||
<type>MW</type>
|
||||
<originID>smi:service.iris.edu/fdsnws/event/1/query?originid=10109854</originID>
|
||||
<creationInfo>
|
||||
<author>GCMT</author>
|
||||
</creationInfo>
|
||||
</magnitude>
|
||||
</event>
|
||||
<event publicID="smi:service.iris.edu/fdsnws/event/1/query?eventid=3282641">
|
||||
<preferredOriginID>smi:service.iris.edu/fdsnws/event/1/query?originid=10082429</preferredOriginID>
|
||||
<preferredMagnitudeID>smi:service.iris.edu/fdsnws/event/1/query?magnitudeid=16795144</preferredMagnitudeID>
|
||||
<type>earthquake</type>
|
||||
<description>
|
||||
<text>CHIAPAS, MEXICO</text>
|
||||
<type>Flinn-Engdahl region</type>
|
||||
</description>
|
||||
<origin publicID="smi:service.iris.edu/fdsnws/event/1/query?originid=10082429">
|
||||
<time>
|
||||
<value>2011-04-07T13:11:23.430000Z</value>
|
||||
</time>
|
||||
<latitude>
|
||||
<value>17.2651</value>
|
||||
</latitude>
|
||||
<longitude>
|
||||
<value>-94.1439</value>
|
||||
</longitude>
|
||||
<depth>
|
||||
<value>165100.0</value>
|
||||
</depth>
|
||||
<creationInfo>
|
||||
<author>ISC</author>
|
||||
</creationInfo>
|
||||
</origin>
|
||||
<magnitude publicID="smi:service.iris.edu/fdsnws/event/1/query?magnitudeid=16795144">
|
||||
<mag>
|
||||
<value>6.7</value>
|
||||
</mag>
|
||||
<type>MW</type>
|
||||
<originID>smi:service.iris.edu/fdsnws/event/1/query?originid=10082436</originID>
|
||||
<creationInfo>
|
||||
<author>GCMT</author>
|
||||
</creationInfo>
|
||||
</magnitude>
|
||||
</event>
|
||||
<event publicID="smi:service.iris.edu/fdsnws/event/1/query?eventid=3281051">
|
||||
<preferredOriginID>smi:service.iris.edu/fdsnws/event/1/query?originid=10053039</preferredOriginID>
|
||||
<preferredMagnitudeID>smi:service.iris.edu/fdsnws/event/1/query?magnitudeid=16759916</preferredMagnitudeID>
|
||||
<type>earthquake</type>
|
||||
<description>
|
||||
<text>FIJI ISLANDS REGION</text>
|
||||
<type>Flinn-Engdahl region</type>
|
||||
</description>
|
||||
<origin publicID="smi:service.iris.edu/fdsnws/event/1/query?originid=10053039">
|
||||
<time>
|
||||
<value>2011-03-31T00:11:58.880000Z</value>
|
||||
</time>
|
||||
<latitude>
|
||||
<value>-16.5479</value>
|
||||
</latitude>
|
||||
<longitude>
|
||||
<value>-177.3915</value>
|
||||
</longitude>
|
||||
<depth>
|
||||
<value>19400.0</value>
|
||||
</depth>
|
||||
<creationInfo>
|
||||
<author>ISC</author>
|
||||
</creationInfo>
|
||||
</origin>
|
||||
<magnitude publicID="smi:service.iris.edu/fdsnws/event/1/query?magnitudeid=16759916">
|
||||
<mag>
|
||||
<value>6.4</value>
|
||||
</mag>
|
||||
<type>MW</type>
|
||||
<originID>smi:service.iris.edu/fdsnws/event/1/query?originid=10053046</originID>
|
||||
<creationInfo>
|
||||
<author>GCMT</author>
|
||||
</creationInfo>
|
||||
</magnitude>
|
||||
</event>
|
||||
<event publicID="smi:service.iris.edu/fdsnws/event/1/query?eventid=3279149">
|
||||
<preferredOriginID>smi:service.iris.edu/fdsnws/event/1/query?originid=9925460</preferredOriginID>
|
||||
<preferredMagnitudeID>smi:service.iris.edu/fdsnws/event/1/query?magnitudeid=16631835</preferredMagnitudeID>
|
||||
<type>earthquake</type>
|
||||
<description>
|
||||
<text>SOUTH SANDWICH ISLANDS REGION</text>
|
||||
<type>Flinn-Engdahl region</type>
|
||||
</description>
|
||||
<origin publicID="smi:service.iris.edu/fdsnws/event/1/query?originid=9925460">
|
||||
<time>
|
||||
<value>2011-03-06T14:32:36.940000Z</value>
|
||||
</time>
|
||||
<latitude>
|
||||
<value>-56.3864</value>
|
||||
</latitude>
|
||||
<longitude>
|
||||
<value>-27.0253</value>
|
||||
</longitude>
|
||||
<depth>
|
||||
<value>92000.0</value>
|
||||
</depth>
|
||||
<creationInfo>
|
||||
<author>ISC</author>
|
||||
</creationInfo>
|
||||
</origin>
|
||||
<magnitude publicID="smi:service.iris.edu/fdsnws/event/1/query?magnitudeid=16631835">
|
||||
<mag>
|
||||
<value>6.5</value>
|
||||
</mag>
|
||||
<type>MW</type>
|
||||
<originID>smi:service.iris.edu/fdsnws/event/1/query?originid=9925462</originID>
|
||||
<creationInfo>
|
||||
<author>GCMT</author>
|
||||
</creationInfo>
|
||||
</magnitude>
|
||||
</event>
|
||||
<event publicID="smi:service.iris.edu/fdsnws/event/1/query?eventid=3278515">
|
||||
<preferredOriginID>smi:service.iris.edu/fdsnws/event/1/query?originid=9916282</preferredOriginID>
|
||||
<preferredMagnitudeID>smi:service.iris.edu/fdsnws/event/1/query?magnitudeid=16620185</preferredMagnitudeID>
|
||||
<type>earthquake</type>
|
||||
<description>
|
||||
<text>EASTER ISLAND REGION</text>
|
||||
<type>Flinn-Engdahl region</type>
|
||||
</description>
|
||||
<origin publicID="smi:service.iris.edu/fdsnws/event/1/query?originid=9916282">
|
||||
<time>
|
||||
<value>2011-03-01T00:53:45.350000Z</value>
|
||||
</time>
|
||||
<latitude>
|
||||
<value>-29.6428</value>
|
||||
</latitude>
|
||||
<longitude>
|
||||
<value>-112.1246</value>
|
||||
</longitude>
|
||||
<depth>
|
||||
<value>3800.0</value>
|
||||
</depth>
|
||||
<creationInfo>
|
||||
<author>ISC</author>
|
||||
</creationInfo>
|
||||
</origin>
|
||||
<magnitude publicID="smi:service.iris.edu/fdsnws/event/1/query?magnitudeid=16620185">
|
||||
<mag>
|
||||
<value>6.1</value>
|
||||
</mag>
|
||||
<type>MW</type>
|
||||
<originID>smi:service.iris.edu/fdsnws/event/1/query?originid=9916288</originID>
|
||||
<creationInfo>
|
||||
<author>GCMT</author>
|
||||
</creationInfo>
|
||||
</magnitude>
|
||||
</event>
|
||||
<event publicID="smi:service.iris.edu/fdsnws/event/1/query?eventid=3278477">
|
||||
<preferredOriginID>smi:service.iris.edu/fdsnws/event/1/query?originid=9851774</preferredOriginID>
|
||||
<preferredMagnitudeID>smi:service.iris.edu/fdsnws/event/1/query?magnitudeid=16541902</preferredMagnitudeID>
|
||||
<type>earthquake</type>
|
||||
<description>
|
||||
<text>OAXACA, MEXICO</text>
|
||||
<type>Flinn-Engdahl region</type>
|
||||
</description>
|
||||
<origin publicID="smi:service.iris.edu/fdsnws/event/1/query?originid=9851774">
|
||||
<time>
|
||||
<value>2011-02-25T13:07:26.980000Z</value>
|
||||
</time>
|
||||
<latitude>
|
||||
<value>17.8214</value>
|
||||
</latitude>
|
||||
<longitude>
|
||||
<value>-95.1708</value>
|
||||
</longitude>
|
||||
<depth>
|
||||
<value>130600.0</value>
|
||||
</depth>
|
||||
<creationInfo>
|
||||
<author>ISC</author>
|
||||
</creationInfo>
|
||||
</origin>
|
||||
<magnitude publicID="smi:service.iris.edu/fdsnws/event/1/query?magnitudeid=16541902">
|
||||
<mag>
|
||||
<value>6.0</value>
|
||||
</mag>
|
||||
<type>MW</type>
|
||||
<originID>smi:service.iris.edu/fdsnws/event/1/query?originid=9851779</originID>
|
||||
<creationInfo>
|
||||
<author>GCMT</author>
|
||||
</creationInfo>
|
||||
</magnitude>
|
||||
</event>
|
||||
<event publicID="smi:service.iris.edu/fdsnws/event/1/query?eventid=3278416">
|
||||
<preferredOriginID>smi:service.iris.edu/fdsnws/event/1/query?originid=9831995</preferredOriginID>
|
||||
<preferredMagnitudeID>smi:service.iris.edu/fdsnws/event/1/query?magnitudeid=16530631</preferredMagnitudeID>
|
||||
<type>earthquake</type>
|
||||
<description>
|
||||
<text>SOUTH ISLAND, NEW ZEALAND</text>
|
||||
<type>Flinn-Engdahl region</type>
|
||||
</description>
|
||||
<origin publicID="smi:service.iris.edu/fdsnws/event/1/query?originid=9831995">
|
||||
<time>
|
||||
<value>2011-02-21T23:51:42.340000Z</value>
|
||||
</time>
|
||||
<latitude>
|
||||
<value>-43.4935</value>
|
||||
</latitude>
|
||||
<longitude>
|
||||
<value>172.713</value>
|
||||
</longitude>
|
||||
<depth>
|
||||
<value>4800.0</value>
|
||||
</depth>
|
||||
<creationInfo>
|
||||
<author>ISC</author>
|
||||
</creationInfo>
|
||||
</origin>
|
||||
<magnitude publicID="smi:service.iris.edu/fdsnws/event/1/query?magnitudeid=16530631">
|
||||
<mag>
|
||||
<value>6.1</value>
|
||||
</mag>
|
||||
<type>MW</type>
|
||||
<originID>smi:service.iris.edu/fdsnws/event/1/query?originid=9832004</originID>
|
||||
<creationInfo>
|
||||
<author>GCMT</author>
|
||||
</creationInfo>
|
||||
</magnitude>
|
||||
</event>
|
||||
<event publicID="smi:service.iris.edu/fdsnws/event/1/query?eventid=3278381">
|
||||
<preferredOriginID>smi:service.iris.edu/fdsnws/event/1/query?originid=9831146</preferredOriginID>
|
||||
<preferredMagnitudeID>smi:service.iris.edu/fdsnws/event/1/query?magnitudeid=16529492</preferredMagnitudeID>
|
||||
<type>earthquake</type>
|
||||
<description>
|
||||
<text>SOUTH OF FIJI ISLANDS</text>
|
||||
<type>Flinn-Engdahl region</type>
|
||||
</description>
|
||||
<origin publicID="smi:service.iris.edu/fdsnws/event/1/query?originid=9831146">
|
||||
<time>
|
||||
<value>2011-02-21T10:57:51.760000Z</value>
|
||||
</time>
|
||||
<latitude>
|
||||
<value>-26.0435</value>
|
||||
</latitude>
|
||||
<longitude>
|
||||
<value>178.4765</value>
|
||||
</longitude>
|
||||
<depth>
|
||||
<value>551800.0</value>
|
||||
</depth>
|
||||
<creationInfo>
|
||||
<author>ISC</author>
|
||||
</creationInfo>
|
||||
</origin>
|
||||
<magnitude publicID="smi:service.iris.edu/fdsnws/event/1/query?magnitudeid=16529492">
|
||||
<mag>
|
||||
<value>6.5</value>
|
||||
</mag>
|
||||
<type>MW</type>
|
||||
<originID>smi:service.iris.edu/fdsnws/event/1/query?originid=9831149</originID>
|
||||
<creationInfo>
|
||||
<author>GCMT</author>
|
||||
</creationInfo>
|
||||
</magnitude>
|
||||
</event>
|
||||
<event publicID="smi:service.iris.edu/fdsnws/event/1/query?eventid=3277925">
|
||||
<preferredOriginID>smi:service.iris.edu/fdsnws/event/1/query?originid=9817429</preferredOriginID>
|
||||
<preferredMagnitudeID>smi:service.iris.edu/fdsnws/event/1/query?magnitudeid=16511317</preferredMagnitudeID>
|
||||
<type>earthquake</type>
|
||||
<description>
|
||||
<text>TONGA ISLANDS</text>
|
||||
<type>Flinn-Engdahl region</type>
|
||||
</description>
|
||||
<origin publicID="smi:service.iris.edu/fdsnws/event/1/query?originid=9817429">
|
||||
<time>
|
||||
<value>2011-02-12T17:57:56.170000Z</value>
|
||||
</time>
|
||||
<latitude>
|
||||
<value>-20.8515</value>
|
||||
</latitude>
|
||||
<longitude>
|
||||
<value>-175.5845</value>
|
||||
</longitude>
|
||||
<depth>
|
||||
<value>85900.0</value>
|
||||
</depth>
|
||||
<creationInfo>
|
||||
<author>ISC</author>
|
||||
</creationInfo>
|
||||
</origin>
|
||||
<magnitude publicID="smi:service.iris.edu/fdsnws/event/1/query?magnitudeid=16511317">
|
||||
<mag>
|
||||
<value>6.1</value>
|
||||
</mag>
|
||||
<type>MW</type>
|
||||
<originID>smi:service.iris.edu/fdsnws/event/1/query?originid=9817432</originID>
|
||||
<creationInfo>
|
||||
<author>GCMT</author>
|
||||
</creationInfo>
|
||||
</magnitude>
|
||||
</event>
|
||||
<event publicID="smi:service.iris.edu/fdsnws/event/1/query?eventid=3277104">
|
||||
<preferredOriginID>smi:service.iris.edu/fdsnws/event/1/query?originid=9794081</preferredOriginID>
|
||||
<preferredMagnitudeID>smi:service.iris.edu/fdsnws/event/1/query?magnitudeid=16481049</preferredMagnitudeID>
|
||||
<type>earthquake</type>
|
||||
<description>
|
||||
<text>TONGA ISLANDS</text>
|
||||
<type>Flinn-Engdahl region</type>
|
||||
</description>
|
||||
<origin publicID="smi:service.iris.edu/fdsnws/event/1/query?originid=9794081">
|
||||
<time>
|
||||
<value>2011-01-31T06:03:26.330000Z</value>
|
||||
</time>
|
||||
<latitude>
|
||||
<value>-21.9987</value>
|
||||
</latitude>
|
||||
<longitude>
|
||||
<value>-175.5367</value>
|
||||
</longitude>
|
||||
<depth>
|
||||
<value>69300.0</value>
|
||||
</depth>
|
||||
<creationInfo>
|
||||
<author>ISC</author>
|
||||
</creationInfo>
|
||||
</origin>
|
||||
<magnitude publicID="smi:service.iris.edu/fdsnws/event/1/query?magnitudeid=16481049">
|
||||
<mag>
|
||||
<value>6.0</value>
|
||||
</mag>
|
||||
<type>MW</type>
|
||||
<originID>smi:service.iris.edu/fdsnws/event/1/query?originid=9794089</originID>
|
||||
<creationInfo>
|
||||
<author>GCMT</author>
|
||||
</creationInfo>
|
||||
</magnitude>
|
||||
</event>
|
||||
</eventParameters>
|
||||
</ns0:quakeml>
|
654
07-Receiver_Functions/receiverFuntionCalculation.ipynb
Normal file
654
07-Receiver_Functions/receiverFuntionCalculation.ipynb
Normal file
File diff suppressed because one or more lines are too long
19
README.md
19
README.md
@ -18,4 +18,21 @@ This has been forked from [Seismo Live](http://seismo-live.org). The source code
|
||||
|
||||
1. Simple Cross Correlation: Use the cross-correlation to detect and determine the time shift of two test events with one template event.
|
||||
2. Enhanced Picker: This has been taken from the [ObsPy Tutorial](https://docs.obspy.org/master/tutorial/code_snippets/xcorr_pick_correction.html). ObsPy is licensed under the [LGPL v3.0](https://www.gnu.de/documents/lgpl-3.0.en.html)
|
||||
3. Ambient Seismic Noise Analysis from [Seismo Live](http://seismo-live.org). The source code is available at https://github.com/krischer/seismo_live (licensed under a ***CC BY-NC-SA 4.0 License***. © 2015-2019 Lion Krischer).
|
||||
3. Ambient Seismic Noise Analysis from [Seismo Live](http://seismo-live.org). The source code is available at https://github.com/krischer/seismo_live (licensed under a ***CC BY-NC-SA 4.0 License***. © 2015-2019 Lion Krischer).
|
||||
|
||||
### 04 - FFT, DFT and Applications
|
||||
|
||||
Interpolation and Deciamtion of Time Series using the DFT
|
||||
|
||||
### 05 - Spectrogram
|
||||
|
||||
1. Analysing dispersive signals. How to visualize changes in frequency content over time (moving window analysis, mutiple filter technique, instantaneous frequency, continuous wavelet transform, the uncertainty problem).
|
||||
2. Filtering time series with non spectral filtering method.
|
||||
|
||||
### 06 - Surface Waves
|
||||
|
||||
Analysing phase and group velocity of recorded earthquake signals at one and two seismological stations.
|
||||
|
||||
### 07 - Receiver Functions
|
||||
|
||||
How to get receiver functions from seismograms.
|
BIN
images/karte3.jpg
Normal file
BIN
images/karte3.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 749 KiB |
BIN
images/phase_velocity_map.png
Normal file
BIN
images/phase_velocity_map.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 22 KiB |
BIN
images/seismogram.png
Normal file
BIN
images/seismogram.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 127 KiB |
BIN
images/stack-surface-waves.png
Normal file
BIN
images/stack-surface-waves.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 28 MiB |
Loading…
Reference in New Issue
Block a user