mt-metadata 0.3.4__py2.py3-none-any.whl → 0.3.5__py2.py3-none-any.whl
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.
Potentially problematic release.
This version of mt-metadata might be problematic. Click here for more details.
- mt_metadata/__init__.py +1 -1
- mt_metadata/base/metadata.py +8 -8
- mt_metadata/timeseries/stationxml/xml_inventory_mt_experiment.py +4 -1
- mt_metadata/timeseries/tools/from_many_mt_files.py +15 -3
- mt_metadata/transfer_functions/processing/aurora/band.py +16 -0
- mt_metadata/transfer_functions/processing/fourier_coefficients/standards/decimation.json +1 -1
- {mt_metadata-0.3.4.dist-info → mt_metadata-0.3.5.dist-info}/METADATA +3 -3
- {mt_metadata-0.3.4.dist-info → mt_metadata-0.3.5.dist-info}/RECORD +12 -13
- {mt_metadata-0.3.4.dist-info → mt_metadata-0.3.5.dist-info}/WHEEL +1 -1
- mt_metadata/timeseries/filters/channel_response_filter.py +0 -476
- {mt_metadata-0.3.4.dist-info → mt_metadata-0.3.5.dist-info}/AUTHORS.rst +0 -0
- {mt_metadata-0.3.4.dist-info → mt_metadata-0.3.5.dist-info}/LICENSE +0 -0
- {mt_metadata-0.3.4.dist-info → mt_metadata-0.3.5.dist-info}/top_level.txt +0 -0
mt_metadata/__init__.py
CHANGED
|
@@ -39,7 +39,7 @@ you should only have to changes these dictionaries.
|
|
|
39
39
|
|
|
40
40
|
__author__ = """Jared Peacock"""
|
|
41
41
|
__email__ = "jpeacock@usgs.gov"
|
|
42
|
-
__version__ = "0.3.
|
|
42
|
+
__version__ = "0.3.5"
|
|
43
43
|
|
|
44
44
|
# =============================================================================
|
|
45
45
|
# Imports
|
mt_metadata/base/metadata.py
CHANGED
|
@@ -300,7 +300,7 @@ class Base:
|
|
|
300
300
|
self.logger.exception(error)
|
|
301
301
|
raise MTSchemaError(error)
|
|
302
302
|
|
|
303
|
-
def _validate_option(self, name, option_list):
|
|
303
|
+
def _validate_option(self, name, value, option_list):
|
|
304
304
|
"""
|
|
305
305
|
validate the given attribute name agains possible options and check
|
|
306
306
|
for aliases
|
|
@@ -315,21 +315,21 @@ class Base:
|
|
|
315
315
|
:rtype: TYPE
|
|
316
316
|
|
|
317
317
|
"""
|
|
318
|
-
if
|
|
318
|
+
if value is None:
|
|
319
319
|
return True, False, None
|
|
320
320
|
options = [ss.lower() for ss in option_list]
|
|
321
321
|
other_possible = False
|
|
322
322
|
if "other" in options:
|
|
323
323
|
other_possible = True
|
|
324
|
-
if
|
|
324
|
+
if value.lower() in options:
|
|
325
325
|
return True, other_possible, None
|
|
326
|
-
elif
|
|
326
|
+
elif value.lower() not in options and other_possible:
|
|
327
327
|
msg = (
|
|
328
|
-
"{
|
|
329
|
-
+ " are allowed. Allowing {
|
|
328
|
+
f"Value '{value}' not found for metadata field '{name}' in options list {option_list}, but other options"
|
|
329
|
+
+ f" are allowed. Allowing {option_list} to be set to {value}."
|
|
330
330
|
)
|
|
331
331
|
return True, other_possible, msg
|
|
332
|
-
return False, other_possible, "{
|
|
332
|
+
return False, other_possible, f"Value '{value}' for metadata field '{name}' not found in options list {option_list}"
|
|
333
333
|
|
|
334
334
|
def __setattr__(self, name, value):
|
|
335
335
|
"""
|
|
@@ -399,7 +399,7 @@ class Base:
|
|
|
399
399
|
# check options
|
|
400
400
|
if v_dict["style"] == "controlled vocabulary":
|
|
401
401
|
options = v_dict["options"]
|
|
402
|
-
accept, other, msg = self._validate_option(value, options)
|
|
402
|
+
accept, other, msg = self._validate_option(name, value, options)
|
|
403
403
|
if not accept:
|
|
404
404
|
self.logger.error(msg.format(value, options))
|
|
405
405
|
raise MTSchemaError(msg.format(value, options))
|
|
@@ -170,7 +170,10 @@ class XMLInventoryMTExperiment:
|
|
|
170
170
|
xml_network = self.network_translator.mt_to_xml(mt_survey)
|
|
171
171
|
for mt_station in mt_survey.stations:
|
|
172
172
|
xml_station = self.station_translator.mt_to_xml(mt_station)
|
|
173
|
-
|
|
173
|
+
if mt_survey.country is not None:
|
|
174
|
+
xml_station.site.country = ",".join(
|
|
175
|
+
[str(country) for country in mt_survey.country]
|
|
176
|
+
)
|
|
174
177
|
for mt_run in mt_station.runs:
|
|
175
178
|
xml_station = self.add_run(
|
|
176
179
|
xml_station, mt_run, mt_survey.filters
|
|
@@ -12,7 +12,14 @@ from pathlib import Path
|
|
|
12
12
|
import pandas as pd
|
|
13
13
|
from xml.etree import cElementTree as et
|
|
14
14
|
|
|
15
|
-
from mt_metadata.timeseries import
|
|
15
|
+
from mt_metadata.timeseries import (
|
|
16
|
+
Experiment,
|
|
17
|
+
Survey,
|
|
18
|
+
Station,
|
|
19
|
+
Run,
|
|
20
|
+
Electric,
|
|
21
|
+
Magnetic,
|
|
22
|
+
)
|
|
16
23
|
|
|
17
24
|
from mt_metadata.timeseries.filters import (
|
|
18
25
|
PoleZeroFilter,
|
|
@@ -22,6 +29,7 @@ from mt_metadata.timeseries.filters import (
|
|
|
22
29
|
)
|
|
23
30
|
from mt_metadata.timeseries.stationxml import XMLInventoryMTExperiment
|
|
24
31
|
|
|
32
|
+
|
|
25
33
|
# =============================================================================
|
|
26
34
|
# Useful Class
|
|
27
35
|
# =============================================================================
|
|
@@ -182,7 +190,7 @@ class MT2StationXML(XMLInventoryMTExperiment):
|
|
|
182
190
|
channels_list = []
|
|
183
191
|
for ch in order:
|
|
184
192
|
for fn in rdf:
|
|
185
|
-
if ch in fn.name.lower():
|
|
193
|
+
if ch in fn.name[len(station) :].lower():
|
|
186
194
|
channels_list.append(fn)
|
|
187
195
|
break
|
|
188
196
|
|
|
@@ -196,7 +204,11 @@ class MT2StationXML(XMLInventoryMTExperiment):
|
|
|
196
204
|
:rtype: TYPE
|
|
197
205
|
|
|
198
206
|
"""
|
|
199
|
-
fn_dict = {
|
|
207
|
+
fn_dict = {
|
|
208
|
+
"survey": self.survey,
|
|
209
|
+
"filters": self.filters,
|
|
210
|
+
"stations": [],
|
|
211
|
+
}
|
|
200
212
|
if stations in [None, []]:
|
|
201
213
|
station_iterator = self.stations
|
|
202
214
|
else:
|
|
@@ -24,6 +24,7 @@ class Band(Base):
|
|
|
24
24
|
def __init__(self, **kwargs):
|
|
25
25
|
|
|
26
26
|
super().__init__(attr_dict=attr_dict, **kwargs)
|
|
27
|
+
self._name = None
|
|
27
28
|
|
|
28
29
|
@property
|
|
29
30
|
def lower_bound(self):
|
|
@@ -41,6 +42,21 @@ class Band(Base):
|
|
|
41
42
|
def upper_closed(self):
|
|
42
43
|
return self.to_interval().closed_right
|
|
43
44
|
|
|
45
|
+
@property
|
|
46
|
+
def name(self):
|
|
47
|
+
"""
|
|
48
|
+
:return: The name of the frequency band (currently defaults to fstring with 6 decimal places.
|
|
49
|
+
:rtype: str
|
|
50
|
+
"""
|
|
51
|
+
if self._name is None:
|
|
52
|
+
self._name = f"{self.center_frequency:.6f}"
|
|
53
|
+
return self._name
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@name.setter
|
|
57
|
+
def name(self, value):
|
|
58
|
+
self._name = value
|
|
59
|
+
|
|
44
60
|
def _indices_from_frequencies(self, frequencies):
|
|
45
61
|
"""
|
|
46
62
|
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"required": true,
|
|
5
5
|
"style": "number",
|
|
6
6
|
"units": null,
|
|
7
|
-
"description": "Decimation level, must be a
|
|
7
|
+
"description": "Decimation level, must be a non-negative integer starting at 0",
|
|
8
8
|
"options": [],
|
|
9
9
|
"alias": [],
|
|
10
10
|
"example": "1",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: mt-metadata
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.5
|
|
4
4
|
Summary: Metadata for magnetotelluric data
|
|
5
5
|
Home-page: https://github.com/kujaku11/mt_metadata
|
|
6
6
|
Author: Jared Peacock
|
|
@@ -28,7 +28,7 @@ Requires-Dist: matplotlib
|
|
|
28
28
|
Requires-Dist: xarray
|
|
29
29
|
Requires-Dist: loguru
|
|
30
30
|
|
|
31
|
-
# mt_metadata version 0.3.
|
|
31
|
+
# mt_metadata version 0.3.5
|
|
32
32
|
Standard MT metadata
|
|
33
33
|
|
|
34
34
|
[](https://pypi.python.org/pypi/mt-metadata)
|
|
@@ -58,7 +58,7 @@ MT Metadata is a project led by [IRIS-PASSCAL MT Software working group](https:/
|
|
|
58
58
|
|
|
59
59
|
Most people will be using the transfer functions, but a lot of that metadata comes from the time series metadata. This module supports both and has tried to make them more or less seamless to reduce complication.
|
|
60
60
|
|
|
61
|
-
* **Version**: 0.3.
|
|
61
|
+
* **Version**: 0.3.5
|
|
62
62
|
* **Free software**: MIT license
|
|
63
63
|
* **Documentation**: https://mt-metadata.readthedocs.io.
|
|
64
64
|
* **Examples**: Click the `Binder` badge above and Jupyter Notebook examples are in **mt_metadata/examples/notebooks** and **docs/source/notebooks**
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
mt_metadata/__init__.py,sha256=
|
|
1
|
+
mt_metadata/__init__.py,sha256=CmllQ7LH3dBndOB3nUPeBxK2sQMDqPyhjhdr2byrOJY,5588
|
|
2
2
|
mt_metadata/base/__init__.py,sha256=bylCGBoJkeytxeQgMnuqivqFAvbyNE5htvP-3yu1GEY,184
|
|
3
3
|
mt_metadata/base/helpers.py,sha256=oS4s7OysTQZ5J8VVPUJYYuVkqviSIlOwnNFKZe5FWIc,19721
|
|
4
|
-
mt_metadata/base/metadata.py,sha256=
|
|
4
|
+
mt_metadata/base/metadata.py,sha256=obOFdU6FPf2o-SbdxOxZ60LvzbI6Cp-bbRXw8KdkBao,27145
|
|
5
5
|
mt_metadata/base/schema.py,sha256=MvZBvy2elYia5VLRXqOV9x0GIklEWLM4qpTdqo820P4,12842
|
|
6
6
|
mt_metadata/data/__init__.py,sha256=PECwullCdCwKGpd1fkQc1jL8CboaSQfy_Cdm-obl-Y8,217
|
|
7
7
|
mt_metadata/data/mt_xml/__init__.py,sha256=fsW8De_Dhrjp55R12F2RH5M6QHoPO3Qf51yR3AFWztg,26
|
|
@@ -75,7 +75,6 @@ mt_metadata/timeseries/time_period.py,sha256=eUFucwMY8Gsoi375pwuPlrg-HCBENU6UJ-s
|
|
|
75
75
|
mt_metadata/timeseries/timing_system.py,sha256=3Uvu_Ihk1b1SyPFMKHWCDe1_BUfiVYphpatRP3wwS-s,839
|
|
76
76
|
mt_metadata/timeseries/filters/__init__.py,sha256=9FaNM3OVQ1dMiAn5JyfSK8QtICqGXH6TaKhPYjbpgj8,474
|
|
77
77
|
mt_metadata/timeseries/filters/channel_response.py,sha256=yxPaukC2yyEJFayniSyH2IPNGgE4oDIFMmTp4o3jJ1I,17297
|
|
78
|
-
mt_metadata/timeseries/filters/channel_response_filter.py,sha256=8qcQF4UUd_W1FVKQcq_4rII1ZR-sImYhwo6ALhtSoWs,15626
|
|
79
78
|
mt_metadata/timeseries/filters/coefficient_filter.py,sha256=HryPmsFGr-SEkox85DBYc3q8A3M4C_Hmjc-CNvm6e-w,3322
|
|
80
79
|
mt_metadata/timeseries/filters/filter_base.py,sha256=IiPhOkVvJUbdboFxWWeKyj1iKdW1HE061nvBdw6G8TM,12885
|
|
81
80
|
mt_metadata/timeseries/filters/filtered.py,sha256=0o3aFYLhtSeBgpgi5ndSsKGbE5tBJhY_wEq5oQblhOg,6979
|
|
@@ -126,11 +125,11 @@ mt_metadata/timeseries/stationxml/fdsn_tools.py,sha256=6H1hZCxf5-skNSjPazMS_wKu4
|
|
|
126
125
|
mt_metadata/timeseries/stationxml/utils.py,sha256=16617e6snyrsNjletGbw-gLYQ2vt-7VfYPokz6dakts,7257
|
|
127
126
|
mt_metadata/timeseries/stationxml/xml_channel_mt_channel.py,sha256=Pkyg3plhwOp02Yl_ymbsWU0eydpE-lA8OqAo-Yoo1HM,20289
|
|
128
127
|
mt_metadata/timeseries/stationxml/xml_equipment_mt_run.py,sha256=yRPk6lhnzkpgARe6lQkU_-vZrTDDmIIeRCTI9Wig9XY,5151
|
|
129
|
-
mt_metadata/timeseries/stationxml/xml_inventory_mt_experiment.py,sha256=
|
|
128
|
+
mt_metadata/timeseries/stationxml/xml_inventory_mt_experiment.py,sha256=cmDJybDh6-JgzEHQM0CuLeDTIKoNnIyn9PnYrzV1RZs,14036
|
|
130
129
|
mt_metadata/timeseries/stationxml/xml_network_mt_survey.py,sha256=RciEmnFGb8kMf1mA1lLn9d0R7WiOW2BeoV1bDB-eJuU,7124
|
|
131
130
|
mt_metadata/timeseries/stationxml/xml_station_mt_station.py,sha256=pelvkiQios4gz8gHebWY1MPSsfBhfTz6uTgC92Yz9-4,11112
|
|
132
131
|
mt_metadata/timeseries/tools/__init__.py,sha256=loPgjYnajbOX2rQTlLBh79cG2eaUNpI3KaCjp7SB4ik,78
|
|
133
|
-
mt_metadata/timeseries/tools/from_many_mt_files.py,sha256=
|
|
132
|
+
mt_metadata/timeseries/tools/from_many_mt_files.py,sha256=rtx5NAPBUmOgrMXUT-YJxznqfI1qdRkS4B2SWjrU_1c,14405
|
|
134
133
|
mt_metadata/transfer_functions/__init__.py,sha256=wpGghfoqFj4nuFOoHeR8PFGQGMzkWvTb3T_KfmMWswQ,42
|
|
135
134
|
mt_metadata/transfer_functions/core.py,sha256=70hxEFszjZzed-OKFcmF-B_Tm_Zy7212dPJPCF0_lMA,79933
|
|
136
135
|
mt_metadata/transfer_functions/io/__init__.py,sha256=8DKEQZrpF0RK_MTjR0_w0UQfgVf9VwuJrzy7eann1N8,215
|
|
@@ -277,7 +276,7 @@ mt_metadata/transfer_functions/io/zonge/metadata/standards/tx.json,sha256=W0ITo4
|
|
|
277
276
|
mt_metadata/transfer_functions/io/zonge/metadata/standards/unit.json,sha256=A2KIoTXBANiqp8Mjxg3VDZbjNPgASnS7QVdK7KOJ0y8,953
|
|
278
277
|
mt_metadata/transfer_functions/processing/__init__.py,sha256=frcCV1k9oG9oKj3dpUqdJg1PxRT2RSN_XKdLCPjaYaY,2
|
|
279
278
|
mt_metadata/transfer_functions/processing/aurora/__init__.py,sha256=zczI5zoF7ceLEdu0gkmsLbBYJ2rxuyWsYIjZtxwQP8g,668
|
|
280
|
-
mt_metadata/transfer_functions/processing/aurora/band.py,sha256=
|
|
279
|
+
mt_metadata/transfer_functions/processing/aurora/band.py,sha256=pBk8fYXhJ0S_lyWH3JQIbUr3ZeDKkU93pybSUy5jtbE,8509
|
|
281
280
|
mt_metadata/transfer_functions/processing/aurora/channel.py,sha256=TZHfy-XgrijdNVd4Sjbq3tjORFsJNZIzwa4_J7tDOHM,773
|
|
282
281
|
mt_metadata/transfer_functions/processing/aurora/channel_nomenclature.py,sha256=GD6teHAtBUKTkGGDWX2clZwk1Fac8ndCOZ3r8TJDGrY,4264
|
|
283
282
|
mt_metadata/transfer_functions/processing/aurora/decimation.py,sha256=AF7vdU-Q7W4yfnJoPDbhDqgxJTtHiLCLEUDLBEzfmFM,785
|
|
@@ -309,7 +308,7 @@ mt_metadata/transfer_functions/processing/fourier_coefficients/decimation.py,sha
|
|
|
309
308
|
mt_metadata/transfer_functions/processing/fourier_coefficients/fc.py,sha256=bN3S_0uoMqXDcJcw_7oYN3YQqsWITD2UX0aRcgdY7fw,10450
|
|
310
309
|
mt_metadata/transfer_functions/processing/fourier_coefficients/fc_channel.py,sha256=HFFo2XoqaKJOae3mtRSMZ80_AZ6wDx0KKq3oBfIbJKg,926
|
|
311
310
|
mt_metadata/transfer_functions/processing/fourier_coefficients/standards/__init__.py,sha256=Y3rdyXKOgxbSh9FQxQCCplsbqxwWmFIGm6yZG1cj0Uw,135
|
|
312
|
-
mt_metadata/transfer_functions/processing/fourier_coefficients/standards/decimation.json,sha256=
|
|
311
|
+
mt_metadata/transfer_functions/processing/fourier_coefficients/standards/decimation.json,sha256=aatnDixbOC_lPsBE26iXuGTxCnT6uBZAU_VX6iLosbg,3772
|
|
313
312
|
mt_metadata/transfer_functions/processing/fourier_coefficients/standards/fc.json,sha256=ZiIvXRkYIU3tOcUKeIhBwZtDIdm4rlnG4zUMdiKzOT0,1523
|
|
314
313
|
mt_metadata/transfer_functions/processing/fourier_coefficients/standards/fc_channel.json,sha256=pdR6r2BQTmrw8udtjaVz0Z9zmeX_Jd1uezuBQfKd7kY,1873
|
|
315
314
|
mt_metadata/transfer_functions/tf/__init__.py,sha256=rJN-VGiGjCWBzFpruDOQitM8v-dfON9tKgZzX8zAhsI,3890
|
|
@@ -331,9 +330,9 @@ mt_metadata/utils/mttime.py,sha256=6XRIlY6eaSwLqNnRTeaDraQz4sk0PN27Z7z6BMeZkbk,1
|
|
|
331
330
|
mt_metadata/utils/summarize.py,sha256=KisJt2PWz1-_FOBH8NQtidgxjdWPAbIDwPzEB197uhs,4109
|
|
332
331
|
mt_metadata/utils/units.py,sha256=OdALLmytoPvjJ8rYf7QsGq1b8nrNt85A8wUhjqRyTOo,6405
|
|
333
332
|
mt_metadata/utils/validators.py,sha256=vj55VvH11A0H9SeUcVy9lJCDKNzwCiMTSra-_Ws1Ojk,16264
|
|
334
|
-
mt_metadata-0.3.
|
|
335
|
-
mt_metadata-0.3.
|
|
336
|
-
mt_metadata-0.3.
|
|
337
|
-
mt_metadata-0.3.
|
|
338
|
-
mt_metadata-0.3.
|
|
339
|
-
mt_metadata-0.3.
|
|
333
|
+
mt_metadata-0.3.5.dist-info/AUTHORS.rst,sha256=3RKy4std2XZQLNF6xYIiA8S5A0bBPqNO7ypJsuEhiN8,706
|
|
334
|
+
mt_metadata-0.3.5.dist-info/LICENSE,sha256=P33RkFPriIBxsgZtVzSn9KxYa2K7Am42OwMV0h_m5e0,1080
|
|
335
|
+
mt_metadata-0.3.5.dist-info/METADATA,sha256=Vz1gxXzTNbwIH3WLimJ5zd2jyQt5QBO5gZHDmWYOWAk,17286
|
|
336
|
+
mt_metadata-0.3.5.dist-info/WHEEL,sha256=DZajD4pwLWue70CAfc7YaxT1wLUciNBvN_TTcvXpltE,110
|
|
337
|
+
mt_metadata-0.3.5.dist-info/top_level.txt,sha256=fxe_q_GEd9h6iR3050ZnrhSfxUSO5pR8u65souS4RWM,12
|
|
338
|
+
mt_metadata-0.3.5.dist-info/RECORD,,
|
|
@@ -1,476 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
==========================
|
|
3
|
-
Channel Response Filter
|
|
4
|
-
==========================
|
|
5
|
-
|
|
6
|
-
Combines all filters for a given channel into a total response that can be used in
|
|
7
|
-
the frequency domain.
|
|
8
|
-
|
|
9
|
-
.. note:: Time Delay filters should be applied in the time domain
|
|
10
|
-
otherwise bad things can happen.
|
|
11
|
-
"""
|
|
12
|
-
# =============================================================================
|
|
13
|
-
# Imports
|
|
14
|
-
# =============================================================================
|
|
15
|
-
from copy import deepcopy
|
|
16
|
-
import numpy as np
|
|
17
|
-
|
|
18
|
-
from mt_metadata.base import Base, get_schema
|
|
19
|
-
from mt_metadata.timeseries.filters.standards import SCHEMA_FN_PATHS
|
|
20
|
-
from mt_metadata.timeseries.filters import (
|
|
21
|
-
PoleZeroFilter,
|
|
22
|
-
CoefficientFilter,
|
|
23
|
-
TimeDelayFilter,
|
|
24
|
-
FrequencyResponseTableFilter,
|
|
25
|
-
FIRFilter,
|
|
26
|
-
)
|
|
27
|
-
from mt_metadata.utils.units import get_unit_object
|
|
28
|
-
from mt_metadata.timeseries.filters.plotting_helpers import plot_response
|
|
29
|
-
from obspy.core import inventory
|
|
30
|
-
|
|
31
|
-
# =============================================================================
|
|
32
|
-
attr_dict = get_schema("channel_response", SCHEMA_FN_PATHS)
|
|
33
|
-
# =============================================================================
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
class ChannelResponseFilter(Base):
|
|
37
|
-
"""
|
|
38
|
-
This class holds a list of all the filters associated with a channel.
|
|
39
|
-
It has methods for combining the responses of all the filters into a total
|
|
40
|
-
response that we will apply to a data segment.
|
|
41
|
-
"""
|
|
42
|
-
|
|
43
|
-
def __init__(self, **kwargs):
|
|
44
|
-
self.filters_list = []
|
|
45
|
-
self.frequencies = np.logspace(-4, 4, 100)
|
|
46
|
-
self.normalization_frequency = None
|
|
47
|
-
|
|
48
|
-
super().__init__(attr_dict=attr_dict)
|
|
49
|
-
for k, v in kwargs.items():
|
|
50
|
-
setattr(self, k, v)
|
|
51
|
-
|
|
52
|
-
def __str__(self):
|
|
53
|
-
lines = ["Filters Included:\n", "=" * 25, "\n"]
|
|
54
|
-
for f in self.filters_list:
|
|
55
|
-
lines.append(f.__str__())
|
|
56
|
-
lines.append(f"\n{'-'*20}\n")
|
|
57
|
-
|
|
58
|
-
return "".join(lines)
|
|
59
|
-
|
|
60
|
-
def __repr__(self):
|
|
61
|
-
return self.__str__()
|
|
62
|
-
|
|
63
|
-
@property
|
|
64
|
-
def filters_list(self):
|
|
65
|
-
"""filters list"""
|
|
66
|
-
return self._filters_list
|
|
67
|
-
|
|
68
|
-
@filters_list.setter
|
|
69
|
-
def filters_list(self, filters_list):
|
|
70
|
-
"""set the filters list and validate the list"""
|
|
71
|
-
self._filters_list = self._validate_filters_list(filters_list)
|
|
72
|
-
self._check_consistency_of_units()
|
|
73
|
-
|
|
74
|
-
@property
|
|
75
|
-
def frequencies(self):
|
|
76
|
-
"""frequencies to estimate filters"""
|
|
77
|
-
return self._frequencies
|
|
78
|
-
|
|
79
|
-
@frequencies.setter
|
|
80
|
-
def frequencies(self, value):
|
|
81
|
-
"""
|
|
82
|
-
Set the frequencies, make sure the input is validated
|
|
83
|
-
|
|
84
|
-
Linear frequencies
|
|
85
|
-
:param value: Linear Frequencies
|
|
86
|
-
:type value: iterable
|
|
87
|
-
|
|
88
|
-
"""
|
|
89
|
-
if value is None:
|
|
90
|
-
self._frequencies = None
|
|
91
|
-
|
|
92
|
-
elif isinstance(value, (list, tuple, np.ndarray)):
|
|
93
|
-
self._frequencies = np.array(value, dtype=float)
|
|
94
|
-
else:
|
|
95
|
-
msg = f"input values must be an list, tuple, or np.ndarray, not {type(value)}"
|
|
96
|
-
self.logger.error(msg)
|
|
97
|
-
raise TypeError(msg)
|
|
98
|
-
|
|
99
|
-
@property
|
|
100
|
-
def names(self):
|
|
101
|
-
"""names of the filters"""
|
|
102
|
-
names = []
|
|
103
|
-
if self.filters_list:
|
|
104
|
-
names = [f.name for f in self.filters_list]
|
|
105
|
-
return names
|
|
106
|
-
|
|
107
|
-
def _validate_filters_list(self, filters_list):
|
|
108
|
-
"""
|
|
109
|
-
make sure the filters list is valid
|
|
110
|
-
|
|
111
|
-
:param filters_list: DESCRIPTION
|
|
112
|
-
:type filters_list: TYPE
|
|
113
|
-
:return: DESCRIPTION
|
|
114
|
-
:rtype: TYPE
|
|
115
|
-
|
|
116
|
-
"""
|
|
117
|
-
ACCEPTABLE_FILTERS = [
|
|
118
|
-
PoleZeroFilter,
|
|
119
|
-
CoefficientFilter,
|
|
120
|
-
TimeDelayFilter,
|
|
121
|
-
FrequencyResponseTableFilter,
|
|
122
|
-
FIRFilter,
|
|
123
|
-
]
|
|
124
|
-
|
|
125
|
-
def is_acceptable_filter(item):
|
|
126
|
-
if isinstance(item, tuple(ACCEPTABLE_FILTERS)):
|
|
127
|
-
return True
|
|
128
|
-
else:
|
|
129
|
-
return False
|
|
130
|
-
|
|
131
|
-
if filters_list in [[], None]:
|
|
132
|
-
return []
|
|
133
|
-
|
|
134
|
-
if not isinstance(filters_list, list):
|
|
135
|
-
msg = f"Input filters list must be a list not {type(filters_list)}"
|
|
136
|
-
self.logger.error(msg)
|
|
137
|
-
raise TypeError(msg)
|
|
138
|
-
|
|
139
|
-
fails = []
|
|
140
|
-
return_list = []
|
|
141
|
-
for item in filters_list:
|
|
142
|
-
if is_acceptable_filter(item):
|
|
143
|
-
return_list.append(item)
|
|
144
|
-
else:
|
|
145
|
-
fails.append(
|
|
146
|
-
f"Item is not an acceptable filter type, {type(item)}"
|
|
147
|
-
)
|
|
148
|
-
|
|
149
|
-
if fails:
|
|
150
|
-
raise TypeError(", ".join(fails))
|
|
151
|
-
|
|
152
|
-
return return_list
|
|
153
|
-
|
|
154
|
-
@property
|
|
155
|
-
def pass_band(self):
|
|
156
|
-
"""estimate pass band for all filters in frequency"""
|
|
157
|
-
if self.frequencies is None:
|
|
158
|
-
raise ValueError(
|
|
159
|
-
"frequencies are None, must be input to calculate pass band"
|
|
160
|
-
)
|
|
161
|
-
pb = []
|
|
162
|
-
for f in self.filters_list:
|
|
163
|
-
if hasattr(f, "pass_band"):
|
|
164
|
-
f_pb = f.pass_band(self.frequencies)
|
|
165
|
-
if f_pb is None:
|
|
166
|
-
continue
|
|
167
|
-
pb.append((f_pb.min(), f_pb.max()))
|
|
168
|
-
|
|
169
|
-
if pb != []:
|
|
170
|
-
pb = np.array(pb)
|
|
171
|
-
return np.array([pb[:, 0].max(), pb[:, 1].min()])
|
|
172
|
-
return None
|
|
173
|
-
|
|
174
|
-
@property
|
|
175
|
-
def normalization_frequency(self):
|
|
176
|
-
"""get normalization frequency from ZPK or FAP filter"""
|
|
177
|
-
|
|
178
|
-
if self._normalization_frequency == 0.0:
|
|
179
|
-
if self.pass_band is not None:
|
|
180
|
-
return np.round(10 ** np.mean(np.log10(self.pass_band)), 3)
|
|
181
|
-
|
|
182
|
-
return self._normalization_frequency
|
|
183
|
-
|
|
184
|
-
@normalization_frequency.setter
|
|
185
|
-
def normalization_frequency(self, value):
|
|
186
|
-
"""Set normalization frequency if input"""
|
|
187
|
-
|
|
188
|
-
self._normalization_frequency = value
|
|
189
|
-
|
|
190
|
-
@property
|
|
191
|
-
def non_delay_filters(self):
|
|
192
|
-
"""
|
|
193
|
-
|
|
194
|
-
:return: all the non-time_delay filters as a list
|
|
195
|
-
|
|
196
|
-
"""
|
|
197
|
-
non_delay_filters = [
|
|
198
|
-
x for x in self.filters_list if x.type != "time delay"
|
|
199
|
-
]
|
|
200
|
-
return non_delay_filters
|
|
201
|
-
|
|
202
|
-
@property
|
|
203
|
-
def delay_filters(self):
|
|
204
|
-
"""
|
|
205
|
-
|
|
206
|
-
:return: all the time delay filters as a list
|
|
207
|
-
|
|
208
|
-
"""
|
|
209
|
-
delay_filters = [x for x in self.filters_list if x.type == "time delay"]
|
|
210
|
-
return delay_filters
|
|
211
|
-
|
|
212
|
-
@property
|
|
213
|
-
def total_delay(self):
|
|
214
|
-
"""
|
|
215
|
-
|
|
216
|
-
:return: the total delay of all filters
|
|
217
|
-
|
|
218
|
-
"""
|
|
219
|
-
delay_filters = self.delay_filters
|
|
220
|
-
total_delay = 0.0
|
|
221
|
-
for delay_filter in delay_filters:
|
|
222
|
-
total_delay += delay_filter.delay
|
|
223
|
-
return total_delay
|
|
224
|
-
|
|
225
|
-
def complex_response(
|
|
226
|
-
self,
|
|
227
|
-
frequencies=None,
|
|
228
|
-
include_delay=False,
|
|
229
|
-
normalize=False,
|
|
230
|
-
include_decimation=True,
|
|
231
|
-
**kwargs,
|
|
232
|
-
):
|
|
233
|
-
"""
|
|
234
|
-
|
|
235
|
-
:param frequencies: frequencies to compute complex response,
|
|
236
|
-
defaults to None
|
|
237
|
-
:type frequencies: np.ndarray, optional
|
|
238
|
-
:param include_delay: include delay in complex response,
|
|
239
|
-
defaults to False
|
|
240
|
-
:type include_delay: bool, optional
|
|
241
|
-
:param normalize: normalize the response to 1, defaults to False
|
|
242
|
-
:type normalize: bool, optional
|
|
243
|
-
:param include_decimation: Include decimation in response,
|
|
244
|
-
defaults to True
|
|
245
|
-
:type include_decimation: bool, optional
|
|
246
|
-
:return: complex response along give frequency array
|
|
247
|
-
:rtype: np.ndarray
|
|
248
|
-
|
|
249
|
-
"""
|
|
250
|
-
|
|
251
|
-
if frequencies is not None:
|
|
252
|
-
self.frequencies = frequencies
|
|
253
|
-
|
|
254
|
-
if include_delay:
|
|
255
|
-
filters_list = deepcopy(self.filters_list)
|
|
256
|
-
else:
|
|
257
|
-
filters_list = deepcopy(self.non_delay_filters)
|
|
258
|
-
|
|
259
|
-
if not include_decimation:
|
|
260
|
-
filters_list = deepcopy(
|
|
261
|
-
[x for x in filters_list if not x.decimation_active]
|
|
262
|
-
)
|
|
263
|
-
|
|
264
|
-
if len(filters_list) == 0:
|
|
265
|
-
# warn that there are no filters associated with channel?
|
|
266
|
-
return np.ones(len(self.frequencies), dtype=complex)
|
|
267
|
-
|
|
268
|
-
result = filters_list[0].complex_response(self.frequencies)
|
|
269
|
-
|
|
270
|
-
for ff in filters_list[1:]:
|
|
271
|
-
result *= ff.complex_response(self.frequencies)
|
|
272
|
-
|
|
273
|
-
if normalize:
|
|
274
|
-
result /= np.max(np.abs(result))
|
|
275
|
-
return result
|
|
276
|
-
|
|
277
|
-
def compute_instrument_sensitivity(
|
|
278
|
-
self, normalization_frequency=None, sig_figs=6
|
|
279
|
-
):
|
|
280
|
-
"""
|
|
281
|
-
Compute the StationXML instrument sensitivity for the given normalization frequency
|
|
282
|
-
|
|
283
|
-
:param normalization_frequency: DESCRIPTION
|
|
284
|
-
:type normalization_frequency: TYPE
|
|
285
|
-
:return: DESCRIPTION
|
|
286
|
-
:rtype: TYPE
|
|
287
|
-
|
|
288
|
-
"""
|
|
289
|
-
if normalization_frequency is not None:
|
|
290
|
-
self.normalization_frequency = normalization_frequency
|
|
291
|
-
sensitivity = 1.0
|
|
292
|
-
for mt_filter in self.filters_list:
|
|
293
|
-
complex_response = mt_filter.complex_response(
|
|
294
|
-
self.normalization_frequency
|
|
295
|
-
)
|
|
296
|
-
sensitivity *= complex_response.astype(complex)
|
|
297
|
-
try:
|
|
298
|
-
sensitivity = np.abs(sensitivity[0])
|
|
299
|
-
except (IndexError, TypeError):
|
|
300
|
-
sensitivity = np.abs(sensitivity)
|
|
301
|
-
|
|
302
|
-
return round(
|
|
303
|
-
sensitivity, sig_figs - int(np.floor(np.log10(abs(sensitivity))))
|
|
304
|
-
)
|
|
305
|
-
|
|
306
|
-
@property
|
|
307
|
-
def units_in(self):
|
|
308
|
-
"""
|
|
309
|
-
:return: the units of the channel
|
|
310
|
-
"""
|
|
311
|
-
if self.filters_list is [] or len(self.filters_list) == 0:
|
|
312
|
-
return None
|
|
313
|
-
|
|
314
|
-
return self.filters_list[0].units_in
|
|
315
|
-
|
|
316
|
-
@property
|
|
317
|
-
def units_out(self):
|
|
318
|
-
"""
|
|
319
|
-
:return: the units of the channel
|
|
320
|
-
"""
|
|
321
|
-
if self.filters_list is [] or len(self.filters_list) == 0:
|
|
322
|
-
return None
|
|
323
|
-
|
|
324
|
-
return self.filters_list[-1].units_out
|
|
325
|
-
|
|
326
|
-
def _check_consistency_of_units(self):
|
|
327
|
-
"""
|
|
328
|
-
confirms that the input and output units of each filter state are consistent
|
|
329
|
-
"""
|
|
330
|
-
if len(self._filters_list) > 1:
|
|
331
|
-
previous_units = self._filters_list[0].units_out
|
|
332
|
-
for mt_filter in self._filters_list[1:]:
|
|
333
|
-
if mt_filter.units_in != previous_units:
|
|
334
|
-
msg = (
|
|
335
|
-
"Unit consistency is incorrect. "
|
|
336
|
-
f"The input units for {mt_filter.name} should be "
|
|
337
|
-
f"{previous_units} not {mt_filter.units_in}"
|
|
338
|
-
)
|
|
339
|
-
self.logger.error(msg)
|
|
340
|
-
raise ValueError(msg)
|
|
341
|
-
previous_units = mt_filter.units_out
|
|
342
|
-
|
|
343
|
-
return True
|
|
344
|
-
|
|
345
|
-
def to_obspy(self, sample_rate=1):
|
|
346
|
-
"""
|
|
347
|
-
Output :class:`obspy.core.inventory.InstrumentSensitivity` object that
|
|
348
|
-
can be used in a stationxml file.
|
|
349
|
-
|
|
350
|
-
:param normalization_frequency: DESCRIPTION
|
|
351
|
-
:type normalization_frequency: TYPE
|
|
352
|
-
:return: DESCRIPTION
|
|
353
|
-
:rtype: TYPE
|
|
354
|
-
|
|
355
|
-
"""
|
|
356
|
-
total_sensitivity = self.compute_instrument_sensitivity()
|
|
357
|
-
|
|
358
|
-
units_in_obj = get_unit_object(self.units_in)
|
|
359
|
-
units_out_obj = get_unit_object(self.units_out)
|
|
360
|
-
|
|
361
|
-
total_response = inventory.Response()
|
|
362
|
-
total_response.instrument_sensitivity = inventory.InstrumentSensitivity(
|
|
363
|
-
total_sensitivity,
|
|
364
|
-
self.normalization_frequency,
|
|
365
|
-
units_in_obj.abbreviation,
|
|
366
|
-
units_out_obj.abbreviation,
|
|
367
|
-
input_units_description=units_in_obj.name,
|
|
368
|
-
output_units_description=units_out_obj.name,
|
|
369
|
-
)
|
|
370
|
-
|
|
371
|
-
for ii, f in enumerate(self.filters_list, 1):
|
|
372
|
-
if f.type in ["coefficient"]:
|
|
373
|
-
if f.units_out not in ["count"]:
|
|
374
|
-
self.logger.debug(
|
|
375
|
-
f"converting CoefficientFilter {f.name} to PZ"
|
|
376
|
-
)
|
|
377
|
-
pz = PoleZeroFilter()
|
|
378
|
-
pz.gain = f.gain
|
|
379
|
-
pz.units_in = f.units_in
|
|
380
|
-
pz.units_out = f.units_out
|
|
381
|
-
pz.comments = f.comments
|
|
382
|
-
pz.name = f.name
|
|
383
|
-
else:
|
|
384
|
-
pz = f
|
|
385
|
-
|
|
386
|
-
total_response.response_stages.append(
|
|
387
|
-
pz.to_obspy(
|
|
388
|
-
stage_number=ii,
|
|
389
|
-
normalization_frequency=self.normalization_frequency,
|
|
390
|
-
sample_rate=sample_rate,
|
|
391
|
-
)
|
|
392
|
-
)
|
|
393
|
-
else:
|
|
394
|
-
total_response.response_stages.append(
|
|
395
|
-
f.to_obspy(
|
|
396
|
-
stage_number=ii,
|
|
397
|
-
normalization_frequency=self.normalization_frequency,
|
|
398
|
-
sample_rate=sample_rate,
|
|
399
|
-
)
|
|
400
|
-
)
|
|
401
|
-
|
|
402
|
-
return total_response
|
|
403
|
-
|
|
404
|
-
def plot_response(
|
|
405
|
-
self,
|
|
406
|
-
frequencies=None,
|
|
407
|
-
x_units="period",
|
|
408
|
-
unwrap=True,
|
|
409
|
-
pb_tol=1e-1,
|
|
410
|
-
interpolation_method="slinear",
|
|
411
|
-
include_delay=False,
|
|
412
|
-
include_decimation=True,
|
|
413
|
-
):
|
|
414
|
-
"""
|
|
415
|
-
Plot the response
|
|
416
|
-
|
|
417
|
-
:param frequencies: frequencies to compute response, defaults to None
|
|
418
|
-
:type frequencies: np.ndarray, optional
|
|
419
|
-
:param x_units: [ period | frequency ], defaults to "period"
|
|
420
|
-
:type x_units: string, optional
|
|
421
|
-
:param unwrap: Unwrap phase, defaults to True
|
|
422
|
-
:type unwrap: bool, optional
|
|
423
|
-
:param pb_tol: pass band tolerance, defaults to 1e-1
|
|
424
|
-
:type pb_tol: float, optional
|
|
425
|
-
:param interpolation_method: Interpolation method see scipy.signal.interpolate
|
|
426
|
-
[ slinear | nearest | cubic | quadratic | ], defaults to "slinear"
|
|
427
|
-
:type interpolation_method: string, optional
|
|
428
|
-
:param include_delay: include delays in response, defaults to False
|
|
429
|
-
:type include_delay: bool, optional
|
|
430
|
-
:param include_decimation: Include decimation in response,
|
|
431
|
-
defaults to True
|
|
432
|
-
:type include_decimation: bool, optional
|
|
433
|
-
|
|
434
|
-
"""
|
|
435
|
-
|
|
436
|
-
if frequencies is not None:
|
|
437
|
-
self.frequencies = frequencies
|
|
438
|
-
|
|
439
|
-
# get only the filters desired
|
|
440
|
-
if include_delay:
|
|
441
|
-
filters_list = deepcopy(self.filters_list)
|
|
442
|
-
else:
|
|
443
|
-
filters_list = deepcopy(self.non_delay_filters)
|
|
444
|
-
|
|
445
|
-
if not include_decimation:
|
|
446
|
-
filters_list = deepcopy(
|
|
447
|
-
[x for x in filters_list if not x.decimation_active]
|
|
448
|
-
)
|
|
449
|
-
|
|
450
|
-
cr_kwargs = {"interpolation_method": interpolation_method}
|
|
451
|
-
|
|
452
|
-
# get response of individual filters
|
|
453
|
-
cr_list = [
|
|
454
|
-
f.complex_response(self.frequencies, **cr_kwargs)
|
|
455
|
-
for f in filters_list
|
|
456
|
-
]
|
|
457
|
-
|
|
458
|
-
# compute total response
|
|
459
|
-
cr_kwargs["include_delay"] = include_delay
|
|
460
|
-
cr_kwargs["include_decimation"] = include_decimation
|
|
461
|
-
complex_response = self.complex_response(self.frequencies, **cr_kwargs)
|
|
462
|
-
|
|
463
|
-
cr_list.append(complex_response)
|
|
464
|
-
labels = [f.name for f in filters_list] + ["Total Response"]
|
|
465
|
-
|
|
466
|
-
# plot with proper attributes.
|
|
467
|
-
kwargs = {
|
|
468
|
-
"title": f"Channel Response: [{', '.join([f.name for f in filters_list])}]",
|
|
469
|
-
"unwrap": unwrap,
|
|
470
|
-
"x_units": x_units,
|
|
471
|
-
"pass_band": self.pass_band,
|
|
472
|
-
"label": labels,
|
|
473
|
-
"normalization_frequency": self.normalization_frequency,
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
plot_response(self.frequencies, cr_list, **kwargs)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|