Coverage for C: \ Users \ peaco \ OneDrive \ Documents \ GitHub \ mth5 \ mth5 \ io \ nims \ response_filters.py: 100%
65 statements
« prev ^ index » next coverage.py v7.13.1, created at 2026-01-10 00:01 -0800
« prev ^ index » next coverage.py v7.13.1, created at 2026-01-10 00:01 -0800
1# -*- coding: utf-8 -*-
2"""
3Created on Fri Sep 2 13:50:51 2022
5@author: jpeacock
6"""
8# =============================================================================
9# Imports
10# =============================================================================
11from mt_metadata.timeseries.filters import (
12 ChannelResponse,
13 CoefficientFilter,
14 PoleZeroFilter,
15 TimeDelayFilter,
16)
19# =============================================================================
22class ResponseError(Exception):
23 pass
26class Response(object):
27 """
28 Common NIMS response filters for electric and magnetic channels
30 """
32 def __init__(self, system_id=None, **kwargs):
33 self.system_id = system_id
34 self.hardware = "PC"
35 self.instrument_type = "backbone"
36 self.sample_rate = 1
38 self.e_conversion_factor = 409600000.0
39 self.h_conversion_factor = 100
41 self.time_delays_dict = {
42 "hp200": {
43 "hx": -0.0055,
44 "hy": -0.0145,
45 "hz": -0.0235,
46 "ex": -0.1525,
47 "ey": -0.0275,
48 },
49 1: {
50 "hx": -0.1920,
51 "hy": -0.2010,
52 "hz": -0.2100,
53 "ex": -0.2850,
54 "ey": -0.2850,
55 },
56 8: {
57 "hx": -0.2455,
58 "hy": -0.2365,
59 "hz": -0.2275,
60 "ex": -0.1525,
61 "ey": -0.1525,
62 },
63 }
65 for key, value in kwargs.items():
66 setattr(self, key, value)
68 @property
69 def magnetic_low_pass(self):
70 """
71 Low pass 3 pole filter
73 :return: DESCRIPTION
74 :rtype: TYPE
76 """
77 return PoleZeroFilter(
78 name="nims_3_pole_butterworth",
79 zeros=[],
80 poles=[
81 complex(-6.283185, 10.882477),
82 complex(-6.283185, -10.882477),
83 complex(-12.566371, 0),
84 ],
85 units_out="Volt",
86 units_in="nanoTesla",
87 normalization_factor=2002.26936395594,
88 )
90 @property
91 def magnetic_conversion(self):
92 """
94 :return: DESCRIPTION
95 :rtype: TYPE
97 """
99 return CoefficientFilter(
100 name="h_analog_to_digital",
101 gain=self.h_conversion_factor,
102 units_in="Volt",
103 units_out="count",
104 )
106 @property
107 def electric_low_pass(self):
108 """
109 5 pole electric low pass filter
110 :return: DESCRIPTION
111 :rtype: TYPE
113 """
114 return PoleZeroFilter(
115 name="nims_5_pole_butterworth",
116 zeros=[],
117 poles=[
118 complex(-3.88301, 11.9519),
119 complex(-3.88301, -11.9519),
120 complex(-10.1662, 7.38651),
121 complex(-10.1662, -7.38651),
122 complex(-12.5664, 0.0),
123 ],
124 units_in="Volt",
125 units_out="Volt",
126 normalization_factor=313383.493219835,
127 )
129 @property
130 def electric_high_pass_pc(self):
131 """
132 1-pole low pass filter for 8 hz instruments
133 :return: DESCRIPTION
134 :rtype: TYPE
136 """
137 return PoleZeroFilter(
138 name="nims_1_pole_butterworth",
139 zeros=[complex(0.0, 0.0)],
140 poles=[complex(-3.333333e-05, 0.0)],
141 normalization_factor=1,
142 units_in="Volt",
143 units_out="Volt",
144 )
146 @property
147 def electric_high_pass_hp(self):
148 """
149 1-pole low pass for 1 hz instuments
150 :return: DESCRIPTION
151 :rtype: TYPE
153 """
154 return PoleZeroFilter(
155 name="nims_1_pole_butterworth",
156 zeros=[complex(0.0, 0.0)],
157 poles=[complex(-1.66667e-04, 0.0)],
158 normalization_factor=1,
159 units_in="Volt",
160 units_out="Volt",
161 )
163 @property
164 def electric_conversion(self):
165 """
166 electric channel conversion from counts to Volts
167 :return: DESCRIPTION
168 :rtype: TYPE
170 """
171 return CoefficientFilter(
172 name="e_analog_to_digital",
173 gain=self.e_conversion_factor,
174 units_in="Volt",
175 units_out="count",
176 )
178 @property
179 def electric_physical_units(self):
180 """
182 :return: DESCRIPTION
183 :rtype: TYPE
185 """
186 return CoefficientFilter(
187 name="to_mt_units",
188 gain=1e-6,
189 units_in="milliVolt per kilometer",
190 units_out="Volt per meter",
191 )
193 def get_electric_high_pass(self, hardware="pc"):
194 """
195 get the electric high pass filter based on the hardware
196 """
198 self.hardware = hardware
199 if "pc" in hardware.lower():
200 return self.electric_high_pass_pc
201 elif "hp" in hardware.lower():
202 return self.electric_high_pass_hp
203 else:
204 raise ResponseError(f"Hardware value {hardware} not understood")
206 def _get_dt_filter(self, channel, sample_rate):
207 """
208 get the DT filter based on channel ans sampling rate
209 """
210 dt_filter = TimeDelayFilter(
211 name=f"{channel}_time_offset",
212 delay=self.time_delays_dict[sample_rate][channel],
213 units_in="count",
214 units_out="count",
215 )
216 return dt_filter
218 def dipole_filter(self, length):
219 """
220 Make a dipole filter
222 :param length: dipole length in meters
223 :type length: TYPE
224 :return: DESCRIPTION
225 :rtype: TYPE
227 """
229 return CoefficientFilter(
230 name=f"dipole_{length:.2f}",
231 gain=length,
232 units_in="Volt per meter",
233 units_out="Volt",
234 )
236 def _get_magnetic_filter(self, channel):
237 """
238 get mag filter, seems to be the same no matter what
239 """
241 return [
242 self.magnetic_low_pass,
243 self.magnetic_conversion,
244 self._get_dt_filter(channel, self.sample_rate),
245 ]
247 def _get_electric_filter(self, channel, dipole_length):
248 """
249 Get electric filter
250 """
252 filter_list = []
253 filter_list.append(self.electric_physical_units)
254 filter_list.append(self.dipole_filter(dipole_length))
255 filter_list.append(self.electric_low_pass)
256 if self.instrument_type in ["backbone"]:
257 filter_list.append(self.get_electric_high_pass(self.hardware))
259 filter_list.append(self.electric_conversion)
260 filter_list.append(self._get_dt_filter(channel, self.sample_rate))
262 return filter_list
264 def get_channel_response(self, channel, dipole_length=1):
265 """
266 Get the full channel response filter
267 :param channel: DESCRIPTION
268 :type channel: TYPE
269 :param dipole_length: DESCRIPTION, defaults to 1
270 :type dipole_length: TYPE, optional
271 :return: DESCRIPTION
272 :rtype: TYPE
274 """
276 if channel[0] in ["e"]:
277 return ChannelResponse(
278 filters_list=self._get_electric_filter(channel, dipole_length)
279 )
281 elif channel[0] in ["b", "h"]:
282 return ChannelResponse(filters_list=self._get_magnetic_filter(channel))
284 else:
285 raise ValueError(f"Channel {channel} not supported.")