DASPy-toolbox 1.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. DASPy-toolbox-1.0.0/DASPy_toolbox.egg-info/PKG-INFO +85 -0
  2. DASPy-toolbox-1.0.0/DASPy_toolbox.egg-info/SOURCES.txt +52 -0
  3. DASPy-toolbox-1.0.0/DASPy_toolbox.egg-info/dependency_links.txt +1 -0
  4. DASPy-toolbox-1.0.0/DASPy_toolbox.egg-info/entry_points.txt +2 -0
  5. DASPy-toolbox-1.0.0/DASPy_toolbox.egg-info/requires.txt +9 -0
  6. DASPy-toolbox-1.0.0/DASPy_toolbox.egg-info/top_level.txt +1 -0
  7. DASPy-toolbox-1.0.0/LICENSE.txt +1 -0
  8. DASPy-toolbox-1.0.0/PKG-INFO +85 -0
  9. DASPy-toolbox-1.0.0/README.md +60 -0
  10. DASPy-toolbox-1.0.0/daspy/__init__.py +4 -0
  11. DASPy-toolbox-1.0.0/daspy/advanced_tools/__init__.py +0 -0
  12. DASPy-toolbox-1.0.0/daspy/advanced_tools/channel.py +354 -0
  13. DASPy-toolbox-1.0.0/daspy/advanced_tools/decomposition.py +165 -0
  14. DASPy-toolbox-1.0.0/daspy/advanced_tools/denoising.py +276 -0
  15. DASPy-toolbox-1.0.0/daspy/advanced_tools/fdct.py +789 -0
  16. DASPy-toolbox-1.0.0/daspy/advanced_tools/strain2vel.py +245 -0
  17. DASPy-toolbox-1.0.0/daspy/basic_tools/__init__.py +0 -0
  18. DASPy-toolbox-1.0.0/daspy/basic_tools/filter.py +257 -0
  19. DASPy-toolbox-1.0.0/daspy/basic_tools/freqattributes.py +117 -0
  20. DASPy-toolbox-1.0.0/daspy/basic_tools/preprocessing.py +238 -0
  21. DASPy-toolbox-1.0.0/daspy/basic_tools/visualization.py +186 -0
  22. DASPy-toolbox-1.0.0/daspy/core/__init__.py +4 -0
  23. DASPy-toolbox-1.0.0/daspy/core/collection.py +279 -0
  24. DASPy-toolbox-1.0.0/daspy/core/dasdatetime.py +72 -0
  25. DASPy-toolbox-1.0.0/daspy/core/example.pkl +0 -0
  26. DASPy-toolbox-1.0.0/daspy/core/make_example.py +32 -0
  27. DASPy-toolbox-1.0.0/daspy/core/read.py +544 -0
  28. DASPy-toolbox-1.0.0/daspy/core/section.py +1319 -0
  29. DASPy-toolbox-1.0.0/daspy/core/write.py +282 -0
  30. DASPy-toolbox-1.0.0/daspy/seismic_detection/__init__.py +1 -0
  31. DASPy-toolbox-1.0.0/daspy/seismic_detection/calc_travel_time.py +23 -0
  32. DASPy-toolbox-1.0.0/daspy/seismic_detection/core.py +119 -0
  33. DASPy-toolbox-1.0.0/daspy/seismic_detection/detection.py +12 -0
  34. DASPy-toolbox-1.0.0/daspy/seismic_detection/gamma/__init__.py +13 -0
  35. DASPy-toolbox-1.0.0/daspy/seismic_detection/gamma/_base.py +549 -0
  36. DASPy-toolbox-1.0.0/daspy/seismic_detection/gamma/_bayesian_mixture.py +875 -0
  37. DASPy-toolbox-1.0.0/daspy/seismic_detection/gamma/_gaussian_mixture.py +866 -0
  38. DASPy-toolbox-1.0.0/daspy/seismic_detection/gamma/app.py +192 -0
  39. DASPy-toolbox-1.0.0/daspy/seismic_detection/gamma/seismic_ops.py +478 -0
  40. DASPy-toolbox-1.0.0/daspy/seismic_detection/gamma/utils.py +512 -0
  41. DASPy-toolbox-1.0.0/daspy/seismic_detection/location.py +266 -0
  42. DASPy-toolbox-1.0.0/daspy/seismic_detection/magnitude.py +43 -0
  43. DASPy-toolbox-1.0.0/daspy/seismic_detection/phase_picking.py +67 -0
  44. DASPy-toolbox-1.0.0/daspy/structure_imaging/__init__.py +0 -0
  45. DASPy-toolbox-1.0.0/daspy/structure_imaging/ambient_noise.py +4 -0
  46. DASPy-toolbox-1.0.0/daspy/structure_imaging/dispersion.py +27 -0
  47. DASPy-toolbox-1.0.0/daspy/structure_imaging/fault_zone.py +59 -0
  48. DASPy-toolbox-1.0.0/daspy/structure_imaging/inversion.py +6 -0
  49. DASPy-toolbox-1.0.0/daspy/traffic_monitoring/JamDetection.py +6 -0
  50. DASPy-toolbox-1.0.0/daspy/traffic_monitoring/SpeedMeasurement.py +6 -0
  51. DASPy-toolbox-1.0.0/daspy/traffic_monitoring/VehicleDetection.py +6 -0
  52. DASPy-toolbox-1.0.0/daspy/traffic_monitoring/__init__.py +0 -0
  53. DASPy-toolbox-1.0.0/setup.cfg +4 -0
  54. DASPy-toolbox-1.0.0/setup.py +46 -0
@@ -0,0 +1,85 @@
1
+ Metadata-Version: 2.1
2
+ Name: DASPy-toolbox
3
+ Version: 1.0.0
4
+ Summary: DASPy is an open-source project dedicated to provide a python package for DAS (Distributed Acoustic Sensing) data processing, which comprises classic seismic data processing techniques and Specialized algorithms for DAS applications.
5
+ Home-page: https://github.com/HMZ-03/DASPy
6
+ Author: Minzhe Hu, Zefeng Li
7
+ Author-email: hmz2018@mail.ustc.edu.cn
8
+ Maintainer: Minzhe Hu
9
+ Maintainer-email: hmz2018@mail.ustc.edu.cn
10
+ License: MIT License
11
+ Classifier: Operating System :: OS Independent
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Programming Language :: Python :: 3
14
+ Requires-Python: >=3.9
15
+ License-File: LICENSE.txt
16
+ Requires-Dist: numpy
17
+ Requires-Dist: scipy>=1.13
18
+ Requires-Dist: matplotlib
19
+ Requires-Dist: geographiclib
20
+ Requires-Dist: pyproj
21
+ Requires-Dist: h5py
22
+ Requires-Dist: segyio
23
+ Requires-Dist: nptdms
24
+ Requires-Dist: tqdm
25
+
26
+ <img src="./website/USTC.svg" height="170" />&emsp;<img src="./website/DAMS.png" height="150" />
27
+
28
+
29
+ ## DASPy
30
+
31
+ DASPy is an open-source project dedicated to provide a python package for DAS (Distributed Acoustic Sensing) data processing.
32
+
33
+ The goal of the DASPy project is to lower the bar of DAS data processing. DASPy includes:
34
+ * Classic seismic data processing techniques, including preprocessing, filter, spectrum analysis, and visualization
35
+ * Specialized algorithms for DAS applications, including denoising, waveform decomposition, channel attribute analysis, and strain-velocity conversion.
36
+
37
+ DASPy is licensed under the MIT License. [An English version of DASPy tutorial](https://daspy-tutorial.readthedocs.io/en/latest/), [a Chinese version of DASPy tutorial](https://daspy-tutorial-cn.readthedocs.io/zh-cn/latest/) and [the DASPy paper](document/srl-2024124.1.pdf) is available. If you have any questions, please contact me via <hmz2018@mail.ustc.edu.cn>.
38
+
39
+ ## Installation
40
+ DASPy is currently running on Linux, Windows and Mac OS.
41
+ DASPy runs on Python 3.9 and up. We recommend you use the latest version of python 3 if possible.
42
+
43
+ ### Pip (recommanded)
44
+ ```
45
+ pip install git+https://github.com/HMZ-03/DASPy.git
46
+ ```
47
+
48
+ If you installed DASPy this way, you can upgrade DASPy with the following command:
49
+
50
+ ```
51
+ pip install --upgrade git+https://github.com/HMZ-03/DASPy.git
52
+ ```
53
+
54
+ ### Conda
55
+ ```
56
+ conda install -c hmz-03 daspy
57
+ ```
58
+
59
+ If an error is reported, please try updating conda:
60
+
61
+ ```
62
+ conda update -n base -c conda-forge conda
63
+ ```
64
+
65
+ ### Manual installation
66
+ 1. Install dependent packages: numpy, scipy >=1.13, matplotlib, geographiclib, pyproj, h5py, segyio, nptdms, tqdm
67
+
68
+ 2. Add DASPy into your Python path.
69
+
70
+ ## Getting started
71
+ ```
72
+ from daspy import read
73
+ sec = read() # load example waveform
74
+ sec.bandpass(1, 15)
75
+ sec.plot()
76
+ ```
77
+ <img src="./website/waveform.png" height="500" />
78
+
79
+ ### Contributing
80
+
81
+ Please see details on how to contribute to the project [here](CONTRIBUTING.md) and [here](CodingStyleGuide.md).
82
+
83
+ ### Reference
84
+
85
+ * Minzhe Hu and Zefeng Li (2024), [DASPy: A Python Toolbox for DAS Seismology](https://pubs.geoscienceworld.org/ssa/srl/article/95/5/3055/645865/DASPy-A-Python-Toolbox-for-DAS-Seismology), *Seismological Research Letters*, 95(5), 3055–3066, doi: `https://doi.org/10.1785/0220240124`.
@@ -0,0 +1,52 @@
1
+ LICENSE.txt
2
+ README.md
3
+ setup.py
4
+ DASPy_toolbox.egg-info/PKG-INFO
5
+ DASPy_toolbox.egg-info/SOURCES.txt
6
+ DASPy_toolbox.egg-info/dependency_links.txt
7
+ DASPy_toolbox.egg-info/entry_points.txt
8
+ DASPy_toolbox.egg-info/requires.txt
9
+ DASPy_toolbox.egg-info/top_level.txt
10
+ daspy/__init__.py
11
+ daspy/advanced_tools/__init__.py
12
+ daspy/advanced_tools/channel.py
13
+ daspy/advanced_tools/decomposition.py
14
+ daspy/advanced_tools/denoising.py
15
+ daspy/advanced_tools/fdct.py
16
+ daspy/advanced_tools/strain2vel.py
17
+ daspy/basic_tools/__init__.py
18
+ daspy/basic_tools/filter.py
19
+ daspy/basic_tools/freqattributes.py
20
+ daspy/basic_tools/preprocessing.py
21
+ daspy/basic_tools/visualization.py
22
+ daspy/core/__init__.py
23
+ daspy/core/collection.py
24
+ daspy/core/dasdatetime.py
25
+ daspy/core/example.pkl
26
+ daspy/core/make_example.py
27
+ daspy/core/read.py
28
+ daspy/core/section.py
29
+ daspy/core/write.py
30
+ daspy/seismic_detection/__init__.py
31
+ daspy/seismic_detection/calc_travel_time.py
32
+ daspy/seismic_detection/core.py
33
+ daspy/seismic_detection/detection.py
34
+ daspy/seismic_detection/location.py
35
+ daspy/seismic_detection/magnitude.py
36
+ daspy/seismic_detection/phase_picking.py
37
+ daspy/seismic_detection/gamma/__init__.py
38
+ daspy/seismic_detection/gamma/_base.py
39
+ daspy/seismic_detection/gamma/_bayesian_mixture.py
40
+ daspy/seismic_detection/gamma/_gaussian_mixture.py
41
+ daspy/seismic_detection/gamma/app.py
42
+ daspy/seismic_detection/gamma/seismic_ops.py
43
+ daspy/seismic_detection/gamma/utils.py
44
+ daspy/structure_imaging/__init__.py
45
+ daspy/structure_imaging/ambient_noise.py
46
+ daspy/structure_imaging/dispersion.py
47
+ daspy/structure_imaging/fault_zone.py
48
+ daspy/structure_imaging/inversion.py
49
+ daspy/traffic_monitoring/JamDetection.py
50
+ daspy/traffic_monitoring/SpeedMeasurement.py
51
+ daspy/traffic_monitoring/VehicleDetection.py
52
+ daspy/traffic_monitoring/__init__.py
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ daspy = daspy.main:main
@@ -0,0 +1,9 @@
1
+ numpy
2
+ scipy>=1.13
3
+ matplotlib
4
+ geographiclib
5
+ pyproj
6
+ h5py
7
+ segyio
8
+ nptdms
9
+ tqdm
@@ -0,0 +1 @@
1
+ daspy/LICENSE.txt
@@ -0,0 +1,85 @@
1
+ Metadata-Version: 2.1
2
+ Name: DASPy-toolbox
3
+ Version: 1.0.0
4
+ Summary: DASPy is an open-source project dedicated to provide a python package for DAS (Distributed Acoustic Sensing) data processing, which comprises classic seismic data processing techniques and Specialized algorithms for DAS applications.
5
+ Home-page: https://github.com/HMZ-03/DASPy
6
+ Author: Minzhe Hu, Zefeng Li
7
+ Author-email: hmz2018@mail.ustc.edu.cn
8
+ Maintainer: Minzhe Hu
9
+ Maintainer-email: hmz2018@mail.ustc.edu.cn
10
+ License: MIT License
11
+ Classifier: Operating System :: OS Independent
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Programming Language :: Python :: 3
14
+ Requires-Python: >=3.9
15
+ License-File: LICENSE.txt
16
+ Requires-Dist: numpy
17
+ Requires-Dist: scipy>=1.13
18
+ Requires-Dist: matplotlib
19
+ Requires-Dist: geographiclib
20
+ Requires-Dist: pyproj
21
+ Requires-Dist: h5py
22
+ Requires-Dist: segyio
23
+ Requires-Dist: nptdms
24
+ Requires-Dist: tqdm
25
+
26
+ <img src="./website/USTC.svg" height="170" />&emsp;<img src="./website/DAMS.png" height="150" />
27
+
28
+
29
+ ## DASPy
30
+
31
+ DASPy is an open-source project dedicated to provide a python package for DAS (Distributed Acoustic Sensing) data processing.
32
+
33
+ The goal of the DASPy project is to lower the bar of DAS data processing. DASPy includes:
34
+ * Classic seismic data processing techniques, including preprocessing, filter, spectrum analysis, and visualization
35
+ * Specialized algorithms for DAS applications, including denoising, waveform decomposition, channel attribute analysis, and strain-velocity conversion.
36
+
37
+ DASPy is licensed under the MIT License. [An English version of DASPy tutorial](https://daspy-tutorial.readthedocs.io/en/latest/), [a Chinese version of DASPy tutorial](https://daspy-tutorial-cn.readthedocs.io/zh-cn/latest/) and [the DASPy paper](document/srl-2024124.1.pdf) is available. If you have any questions, please contact me via <hmz2018@mail.ustc.edu.cn>.
38
+
39
+ ## Installation
40
+ DASPy is currently running on Linux, Windows and Mac OS.
41
+ DASPy runs on Python 3.9 and up. We recommend you use the latest version of python 3 if possible.
42
+
43
+ ### Pip (recommanded)
44
+ ```
45
+ pip install git+https://github.com/HMZ-03/DASPy.git
46
+ ```
47
+
48
+ If you installed DASPy this way, you can upgrade DASPy with the following command:
49
+
50
+ ```
51
+ pip install --upgrade git+https://github.com/HMZ-03/DASPy.git
52
+ ```
53
+
54
+ ### Conda
55
+ ```
56
+ conda install -c hmz-03 daspy
57
+ ```
58
+
59
+ If an error is reported, please try updating conda:
60
+
61
+ ```
62
+ conda update -n base -c conda-forge conda
63
+ ```
64
+
65
+ ### Manual installation
66
+ 1. Install dependent packages: numpy, scipy >=1.13, matplotlib, geographiclib, pyproj, h5py, segyio, nptdms, tqdm
67
+
68
+ 2. Add DASPy into your Python path.
69
+
70
+ ## Getting started
71
+ ```
72
+ from daspy import read
73
+ sec = read() # load example waveform
74
+ sec.bandpass(1, 15)
75
+ sec.plot()
76
+ ```
77
+ <img src="./website/waveform.png" height="500" />
78
+
79
+ ### Contributing
80
+
81
+ Please see details on how to contribute to the project [here](CONTRIBUTING.md) and [here](CodingStyleGuide.md).
82
+
83
+ ### Reference
84
+
85
+ * Minzhe Hu and Zefeng Li (2024), [DASPy: A Python Toolbox for DAS Seismology](https://pubs.geoscienceworld.org/ssa/srl/article/95/5/3055/645865/DASPy-A-Python-Toolbox-for-DAS-Seismology), *Seismological Research Letters*, 95(5), 3055–3066, doi: `https://doi.org/10.1785/0220240124`.
@@ -0,0 +1,60 @@
1
+ <img src="./website/USTC.svg" height="170" />&emsp;<img src="./website/DAMS.png" height="150" />
2
+
3
+
4
+ ## DASPy
5
+
6
+ DASPy is an open-source project dedicated to provide a python package for DAS (Distributed Acoustic Sensing) data processing.
7
+
8
+ The goal of the DASPy project is to lower the bar of DAS data processing. DASPy includes:
9
+ * Classic seismic data processing techniques, including preprocessing, filter, spectrum analysis, and visualization
10
+ * Specialized algorithms for DAS applications, including denoising, waveform decomposition, channel attribute analysis, and strain-velocity conversion.
11
+
12
+ DASPy is licensed under the MIT License. [An English version of DASPy tutorial](https://daspy-tutorial.readthedocs.io/en/latest/), [a Chinese version of DASPy tutorial](https://daspy-tutorial-cn.readthedocs.io/zh-cn/latest/) and [the DASPy paper](document/srl-2024124.1.pdf) is available. If you have any questions, please contact me via <hmz2018@mail.ustc.edu.cn>.
13
+
14
+ ## Installation
15
+ DASPy is currently running on Linux, Windows and Mac OS.
16
+ DASPy runs on Python 3.9 and up. We recommend you use the latest version of python 3 if possible.
17
+
18
+ ### Pip (recommanded)
19
+ ```
20
+ pip install git+https://github.com/HMZ-03/DASPy.git
21
+ ```
22
+
23
+ If you installed DASPy this way, you can upgrade DASPy with the following command:
24
+
25
+ ```
26
+ pip install --upgrade git+https://github.com/HMZ-03/DASPy.git
27
+ ```
28
+
29
+ ### Conda
30
+ ```
31
+ conda install -c hmz-03 daspy
32
+ ```
33
+
34
+ If an error is reported, please try updating conda:
35
+
36
+ ```
37
+ conda update -n base -c conda-forge conda
38
+ ```
39
+
40
+ ### Manual installation
41
+ 1. Install dependent packages: numpy, scipy >=1.13, matplotlib, geographiclib, pyproj, h5py, segyio, nptdms, tqdm
42
+
43
+ 2. Add DASPy into your Python path.
44
+
45
+ ## Getting started
46
+ ```
47
+ from daspy import read
48
+ sec = read() # load example waveform
49
+ sec.bandpass(1, 15)
50
+ sec.plot()
51
+ ```
52
+ <img src="./website/waveform.png" height="500" />
53
+
54
+ ### Contributing
55
+
56
+ Please see details on how to contribute to the project [here](CONTRIBUTING.md) and [here](CodingStyleGuide.md).
57
+
58
+ ### Reference
59
+
60
+ * Minzhe Hu and Zefeng Li (2024), [DASPy: A Python Toolbox for DAS Seismology](https://pubs.geoscienceworld.org/ssa/srl/article/95/5/3055/645865/DASPy-A-Python-Toolbox-for-DAS-Seismology), *Seismological Research Letters*, 95(5), 3055–3066, doi: `https://doi.org/10.1785/0220240124`.
@@ -0,0 +1,4 @@
1
+ from daspy.core.section import Section
2
+ from daspy.core.collection import Collection
3
+ from daspy.core.read import read
4
+ from daspy.core.dasdatetime import DASDateTime, local_tz, utc
File without changes
@@ -0,0 +1,354 @@
1
+ # Purpose: Several functions for analysis data quality and geometry of channels
2
+ # Author: Minzhe Hu, Zefeng Li
3
+ # Date: 2024.10.11
4
+ # Email: hmz2018@mail.ustc.edu.cn
5
+ import numpy as np
6
+ from copy import deepcopy
7
+ from geographiclib.geodesic import Geodesic
8
+ from pyproj import Proj
9
+
10
+
11
+ def robust_polyfit(data, deg, thresh):
12
+ """
13
+ Fit a curve with a robust weighted polynomial.
14
+
15
+ :param data: 1-dimensional array.
16
+ :param deg: int. Degree of the fitting polynomial
17
+ :param thresh: int or float. Defined MAD multiple of outliers.
18
+ :return: Fitting data
19
+ """
20
+ nch = len(data)
21
+ channels = np.arange(nch)
22
+ p_coef = np.polyfit(channels, data, deg)
23
+ p_fit = np.poly1d(p_coef)
24
+ old_data = p_fit(channels)
25
+ mse = 1
26
+
27
+ # robust fitting until the fitting curve changes < 0.1% at every point.
28
+ while mse > 0.001:
29
+ rsl = abs(data - old_data)
30
+ mad = np.median(rsl)
31
+ weights = np.zeros(nch)
32
+ weights[rsl < thresh * mad] = 1
33
+ p_coef = np.polyfit(channels, data, deg, w=weights)
34
+ p_fit = np.poly1d(p_coef)
35
+ new_data = p_fit(channels)
36
+ mse = np.nanmax(np.abs((new_data - old_data) / old_data))
37
+ old_data = new_data
38
+
39
+ return new_data, weights
40
+
41
+
42
+ def _continuity_checking(lst1, lst2, adjacent=2, toleration=2):
43
+ lst1_raw = deepcopy(lst1)
44
+ for chn in lst1_raw:
45
+ discont = [a for a in lst2 if abs(a - chn) <= adjacent]
46
+ if len(discont) >= adjacent * 2 + 1 - toleration:
47
+ lst1.remove(chn)
48
+ lst2.append(chn)
49
+
50
+ return lst1, lst2
51
+
52
+
53
+ def channel_checking(data, deg=10, thresh=5, continuity=True, adjacent=2,
54
+ toleration=2, mode='low', verbose=False):
55
+ """
56
+ Use the energy of each channel to determine which channels are bad.
57
+
58
+ :param data: 2-dimensional np.ndarray. Axis 0 is channel number and axis 1 is
59
+ time series
60
+ :param deg: int. Degree of the fitting polynomial
61
+ :param thresh: int or float. The MAD multiple of bad channel energy lower
62
+ than good channels.
63
+ :param continuity: bool. Perform continuity checks on bad channels and good
64
+ channels.
65
+ :param adjacent: int. The number of nearby channels for continuity checks.
66
+ :param toleration: int. The number of discontinuous channel allowed in each
67
+ channel (including itself) in the continuity check.
68
+ :param mode: str. 'low' means bad channels have low amplitude, 'high' means
69
+ bad channels have high amplitude, and 'both' means bad channels are
70
+ likely to have low or high amplitude.
71
+ :return: Good channels and bad channels.
72
+ """
73
+ nch = len(data)
74
+ energy = np.log10(np.sum(data**2, axis=1))
75
+
76
+ # Remove abnormal value by robust polynomial fitting.
77
+ fitted_energy, weights = robust_polyfit(energy, deg, thresh)
78
+ deviation = energy - fitted_energy
79
+
80
+ # Iterate eliminates outliers.
81
+ mad = np.median(abs(deviation[weights > 0]))
82
+ if mode == 'low':
83
+ bad_chn = np.argwhere(deviation < -thresh * mad).ravel().tolist()
84
+ elif mode == 'high':
85
+ bad_chn = np.argwhere(deviation > thresh * mad).ravel().tolist()
86
+ elif mode == 'high':
87
+ bad_chn = np.argwhere(deviation < -thresh * mad).ravel().tolist() + \
88
+ np.argwhere(deviation > thresh * mad).ravel().tolist()
89
+ good_chn = list(set(range(nch)) - set(bad_chn))
90
+
91
+ if continuity:
92
+ # Discontinuous normal value are part of bad channels.
93
+ good_chn, bad_chn = _continuity_checking(good_chn, bad_chn,
94
+ adjacent=adjacent,
95
+ toleration=toleration)
96
+
97
+ # Discontinuous outliers are usually not bad channels.
98
+ bad_chn, good_chn = _continuity_checking(bad_chn, good_chn,
99
+ adjacent=adjacent,
100
+ toleration=toleration)
101
+
102
+ bad_chn = np.sort(np.array(bad_chn))
103
+ good_chn = np.sort(np.array(good_chn))
104
+ if verbose:
105
+ return good_chn, bad_chn, energy, fitted_energy - thresh * mad
106
+
107
+ return good_chn, bad_chn
108
+
109
+
110
+ def _channel_location(track_pt):
111
+ track, tn = track_pt[:, :-1], track_pt[:, -1]
112
+ dim = track.shape[1]
113
+ l_track = np.sqrt(np.sum(np.diff(track, axis=0) ** 2, axis=1))
114
+ l_track_cum = np.hstack(([0], np.cumsum(l_track)))
115
+ idx_kp = np.where(tn >= 0)[0]
116
+
117
+ interp_ch = []
118
+ chn = np.floor(tn[idx_kp[0]])
119
+ if abs(chn - tn[idx_kp[0]]) < 1e-6:
120
+ interp_ch.append([*track[idx_kp[0]], chn])
121
+
122
+ seg_interval = []
123
+ for i in range(1, len(idx_kp)):
124
+ # calculate actual interval between known-channel points
125
+ istart, iend = idx_kp[i - 1], idx_kp[i]
126
+ n_chn_kp = tn[iend] - tn[istart]
127
+ d_interp = (l_track_cum[iend] - l_track_cum[istart]) / n_chn_kp
128
+ seg_interval.append([tn[istart], tn[iend], d_interp])
129
+
130
+ l_res = 0 # remaining fiber length before counting the next segment
131
+ # consider if the given channelnumber is not an integer
132
+ chn_res = tn[istart] - int(tn[istart])
133
+ for j in range(istart, iend):
134
+ l_start = l_track[j] + l_res
135
+
136
+ # if tp segment length is large for more than one interval, get the
137
+ # channel loc
138
+ if l_start >= d_interp * (1 - chn_res):
139
+ # floor int, num of channel available
140
+ n_chn_tp = int(l_start / d_interp + chn_res)
141
+ l_new = (np.arange(n_chn_tp) + 1 - chn_res) * d_interp - \
142
+ l_res # channel distance from segment start
143
+
144
+ # interpolate the channel loc
145
+ t_new = np.zeros((len(l_new), dim))
146
+ for d in range(dim):
147
+ t_new[:, d] = np.interp(l_new, [0, l_track[j]],
148
+ [track[j, d], track[j + 1, d]])
149
+
150
+ # remaining length to add to next segment
151
+ l_res = l_start - n_chn_tp * d_interp
152
+
153
+ # write interpolated channel loc
154
+ for ti in t_new:
155
+ chn += 1
156
+ interp_ch.append([*ti, chn])
157
+
158
+ # handle floor int problem when l_start/d_interp is near an
159
+ # interger
160
+ if (d_interp - l_res) / d_interp < 1e-6:
161
+ chn += 1
162
+ interp_ch.append([*track[j + 1, :], int(tn[j + 1])])
163
+ l_res = 0
164
+ chn_res = 0
165
+ # if tp segment length is not enough for one interval, simply add
166
+ # the length to next segment
167
+ elif l_start < d_interp:
168
+ l_res = l_start
169
+
170
+ return np.array(seg_interval), np.array(interp_ch)
171
+
172
+
173
+ def location_interpolation(known_pt, track_pt=None, dx=2, data_type='lonlat',
174
+ verbose=False):
175
+ """
176
+ Interpolate to obtain the positions of all channels.
177
+
178
+ :param known_pt: np.ndarray. Points with known channel numbers. Each row
179
+ includes 2 or 3 coordinates and a channel number.
180
+ :param track_pt: np.ndarray. Optional fiber spatial track points without
181
+ channel numbers. Each row includes 2 or 3 coordinates. Please ensure
182
+ that the track points are arranged in increasing order of track number.
183
+ If track points is not dense enough, please insert the coordinates of
184
+ known points into track points in order.
185
+ :param dx: Known points far from the track (> dx) will be excluded.
186
+ Recommended setting is channel interval. The unit is m.
187
+ :param data_type: str. Coordinate type. 'lonlat' ('lonlatheight') for
188
+ longitude, latitude in degree (and height in meters), 'xy' ('xyz') for
189
+ x, y (and z) in meters.
190
+ :param verbose: bool. If True, return interpoleted channel location and
191
+ segment interval.
192
+ :return: Interpoleted channel location if verbose is False.
193
+ """
194
+ known_pt = known_pt[known_pt[:,-1].argsort()]
195
+ dim = known_pt.shape[1] - 1
196
+ if 'lonlat' in data_type:
197
+ zone = np.floor((max(known_pt[:,0]) + min(known_pt[:,0])) / 2 / 6)\
198
+ .astype(int) + 31
199
+ DASProj = Proj(proj='utm', zone=zone, ellps='WGS84',
200
+ preserve_units=False)
201
+ known_pt[:, 0], known_pt[:, 1] = DASProj(known_pt[:, 0], known_pt[:, 1])
202
+ else:
203
+ assert 'xy' in data_type, ('data_type should be \'lonlat\',\''
204
+ 'lonlatheight\', \'xy\' or \'xyz\'')
205
+
206
+ if track_pt is None:
207
+ seg_interval, interp_ch = _channel_location(known_pt)
208
+ else:
209
+ K = len(known_pt)
210
+ T = len(track_pt)
211
+ track_pt = np.c_[track_pt, np.zeros(T) - 1]
212
+ if 'lonlat' in data_type:
213
+ track_pt[:, 0], track_pt[:, 1] = DASProj(track_pt[:, 0],
214
+ track_pt[:, 1])
215
+
216
+ # insert the known points into the fiber track data
217
+ matrix = [np.tile(track_pt[:, d], (K, 1)) -
218
+ np.tile(known_pt[:, d], (T, 1)).T for d in range(dim)]
219
+
220
+ dist = np.sqrt(np.sum(np.array(matrix) ** 2, axis=0))
221
+ for k in range(K):
222
+ if min(dist[k]) < dx:
223
+ t_list = np.sort(np.where(dist[k] == min(dist[k]))[0])
224
+ for t in t_list:
225
+ if track_pt[t, -1] == -1:
226
+ track_pt[t, -1] = known_pt[k, -1]
227
+ last_pt = t
228
+ break
229
+
230
+ # interpolation with regular spacing along the fiber track
231
+ try:
232
+ track_pt = track_pt[:last_pt + 1]
233
+ except NameError:
234
+ print('All known points are too far away from the track points. If '
235
+ 'they are reliable, they can be merged in sequence as track '
236
+ 'points to input')
237
+ return None
238
+
239
+ seg_interval, interp_ch = _channel_location(track_pt)
240
+
241
+ if data_type == 'lonlat':
242
+ interp_ch[:, 0], interp_ch[:, 1] = \
243
+ DASProj(interp_ch[:, 0], interp_ch[:, 1], inverse=True)
244
+
245
+ if verbose:
246
+ return interp_ch, seg_interval
247
+ return interp_ch
248
+
249
+
250
+ def _xcorr(x, y):
251
+ N = len(x)
252
+ meanx = np.mean(x)
253
+ meany = np.mean(y)
254
+ stdx = np.std(np.asarray(x))
255
+ stdy = np.std(np.asarray(y))
256
+ c = np.sum((y - meany) * (x - meanx)) / (N * stdx * stdy)
257
+ return c
258
+
259
+
260
+ def _horizontal_angle_change(geo, gap=10):
261
+ nch = len(geo)
262
+ angle = np.zeros(nch)
263
+ for i in range(1, nch - 1):
264
+ lon, lat = geo[i]
265
+ lon_s, lat_s = geo[max(i - gap, 0)]
266
+ lon_e, lat_e = geo[min(i + gap, nch - 1)]
267
+ azi_s = Geodesic.WGS84.Inverse(lat_s, lon_s, lat, lon)['azi1']
268
+ azi_e = Geodesic.WGS84.Inverse(lat, lon, lat_e, lon_e)['azi1']
269
+ dazi = azi_e - azi_s
270
+ if abs(dazi) > 180:
271
+ dazi = -np.sign(dazi) * (360 - abs(dazi))
272
+ angle[i] = dazi
273
+
274
+ return angle
275
+
276
+
277
+ def _vertical_angle_change(geo, gap=10):
278
+ nch = len(geo)
279
+ angle = np.zeros(nch)
280
+ for i in range(1, nch - 1):
281
+ lon, lat, dep = geo[i]
282
+ lon_s, lat_s, dep_s = geo[max(i - gap, 0)]
283
+ lon_e, lat_e, dep_e = geo[min(i + gap, nch - 1)]
284
+ s12_s = Geodesic.WGS84.Inverse(lat_s, lon_s, lat, lon)['s12']
285
+ theta_s = np.arctan((dep - dep_s) / s12_s) / np.pi * 180
286
+ s12_e = Geodesic.WGS84.Inverse(lat, lon, lat_e, lon_e)['s12']
287
+ theta_e = np.arctan((dep_e - dep) / s12_e) / np.pi * 180
288
+ angle[i] = theta_e - theta_s
289
+
290
+ return angle
291
+
292
+
293
+ def _local_maximum_indexes(data, thresh):
294
+ idx = np.where(data > thresh)[0]
295
+ if len(idx):
296
+ i = list(np.where(np.diff(idx) > 1)[0] + 1)
297
+ if len(idx) - 1 not in i:
298
+ i.append(len(idx) - 1)
299
+ b = 0
300
+ max_idx = []
301
+ for e in i:
302
+ max_idx.append(idx[b] + np.argmax(data[idx[b]:idx[e]]))
303
+ b = e
304
+ return max_idx
305
+ else:
306
+ return []
307
+
308
+
309
+ def turning_points(data, data_type='coordinate', thresh=5, depth_info=False,
310
+ channel_gap=3):
311
+ """
312
+ Seek turning points in the DAS channel.
313
+
314
+ :param data: numpy.ndarray. Data used to seek turning points.
315
+ :param data_type: str. If data_type is 'coordinate', data should include
316
+ longitude and latitude (first two columns), and can also include depth
317
+ (last column). If data_type is 'waveform', data should be continuous
318
+ waveform, preferably containing signal with strong coherence
319
+ (earthquake, traffic signal, etc.).
320
+ :param thresh: For coordinate data, when the angle of the optical cables on
321
+ both sides centered on a certain point exceeds thresh, it is considered
322
+ an turning point. For waveform, thresh means the MAD multiple of
323
+ adjacent channel cross-correlation values lower than their median.
324
+ :param depth_info: bool. Optional if data_type is 'coordinate'. Whether
325
+ depth (in meters) is included in the coordinate data and need to be
326
+ used.
327
+ :param channel_gap: int. Optional if data_type is 'coordinate'. The smaller
328
+ the value is, the finer the segmentation will be. It is recommended to
329
+ set it to half the ratio of gauge length and channel interval.
330
+ :return: list. Channel index of turning points.
331
+ """
332
+ if data_type == 'coordinate':
333
+ angle = _horizontal_angle_change(data[:, :2], gap=channel_gap)
334
+ turning_h = _local_maximum_indexes(abs(angle), thresh)
335
+
336
+ if depth_info:
337
+ angle = _vertical_angle_change(data, gap=channel_gap)
338
+ turning_v = _local_maximum_indexes(abs(angle), thresh)
339
+ return turning_h, turning_v
340
+
341
+ return turning_h
342
+
343
+ elif data_type == 'waveform':
344
+ nch = len(data)
345
+ cc = np.zeros(nch - 1)
346
+ for i in range(nch - 1):
347
+ cc[i] = _xcorr(data[i], data[i + 1])
348
+ median = np.median(cc)
349
+ mad = np.median(abs(cc - median))
350
+
351
+ return np.argwhere(cc < median - thresh * mad)[0]
352
+
353
+ else:
354
+ raise ValueError('Data_type should be \'coordinate\' or \'waveform\'.')