DASPy-toolbox 1.2.1__tar.gz → 1.2.3__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.
- {daspy_toolbox-1.2.1 → daspy_toolbox-1.2.3}/DASPy_toolbox.egg-info/PKG-INFO +1 -1
- {daspy_toolbox-1.2.1 → daspy_toolbox-1.2.3}/PKG-INFO +1 -1
- {daspy_toolbox-1.2.1 → daspy_toolbox-1.2.3}/daspy/advanced_tools/channel.py +3 -2
- {daspy_toolbox-1.2.1 → daspy_toolbox-1.2.3}/daspy/advanced_tools/strain2vel.py +19 -17
- {daspy_toolbox-1.2.1 → daspy_toolbox-1.2.3}/daspy/basic_tools/freqattributes.py +11 -1
- {daspy_toolbox-1.2.1 → daspy_toolbox-1.2.3}/daspy/basic_tools/preprocessing.py +4 -4
- {daspy_toolbox-1.2.1 → daspy_toolbox-1.2.3}/daspy/basic_tools/visualization.py +10 -9
- {daspy_toolbox-1.2.1 → daspy_toolbox-1.2.3}/daspy/core/collection.py +31 -9
- {daspy_toolbox-1.2.1 → daspy_toolbox-1.2.3}/daspy/core/dasdatetime.py +2 -2
- {daspy_toolbox-1.2.1 → daspy_toolbox-1.2.3}/daspy/core/read.py +122 -14
- {daspy_toolbox-1.2.1 → daspy_toolbox-1.2.3}/daspy/core/section.py +23 -11
- {daspy_toolbox-1.2.1 → daspy_toolbox-1.2.3}/daspy/core/util.py +16 -5
- {daspy_toolbox-1.2.1 → daspy_toolbox-1.2.3}/daspy/core/write.py +108 -9
- {daspy_toolbox-1.2.1 → daspy_toolbox-1.2.3}/setup.py +1 -1
- {daspy_toolbox-1.2.1 → daspy_toolbox-1.2.3}/DASPy_toolbox.egg-info/SOURCES.txt +0 -0
- {daspy_toolbox-1.2.1 → daspy_toolbox-1.2.3}/DASPy_toolbox.egg-info/dependency_links.txt +0 -0
- {daspy_toolbox-1.2.1 → daspy_toolbox-1.2.3}/DASPy_toolbox.egg-info/entry_points.txt +0 -0
- {daspy_toolbox-1.2.1 → daspy_toolbox-1.2.3}/DASPy_toolbox.egg-info/requires.txt +0 -0
- {daspy_toolbox-1.2.1 → daspy_toolbox-1.2.3}/DASPy_toolbox.egg-info/top_level.txt +0 -0
- {daspy_toolbox-1.2.1 → daspy_toolbox-1.2.3}/LICENSE +0 -0
- {daspy_toolbox-1.2.1 → daspy_toolbox-1.2.3}/README.md +0 -0
- {daspy_toolbox-1.2.1 → daspy_toolbox-1.2.3}/daspy/__init__.py +0 -0
- {daspy_toolbox-1.2.1 → daspy_toolbox-1.2.3}/daspy/advanced_tools/__init__.py +0 -0
- {daspy_toolbox-1.2.1 → daspy_toolbox-1.2.3}/daspy/advanced_tools/decomposition.py +0 -0
- {daspy_toolbox-1.2.1 → daspy_toolbox-1.2.3}/daspy/advanced_tools/denoising.py +0 -0
- {daspy_toolbox-1.2.1 → daspy_toolbox-1.2.3}/daspy/advanced_tools/fdct.py +0 -0
- {daspy_toolbox-1.2.1 → daspy_toolbox-1.2.3}/daspy/basic_tools/__init__.py +0 -0
- {daspy_toolbox-1.2.1 → daspy_toolbox-1.2.3}/daspy/basic_tools/filter.py +0 -0
- {daspy_toolbox-1.2.1 → daspy_toolbox-1.2.3}/daspy/core/__init__.py +0 -0
- {daspy_toolbox-1.2.1 → daspy_toolbox-1.2.3}/daspy/core/example.pkl +0 -0
- {daspy_toolbox-1.2.1 → daspy_toolbox-1.2.3}/daspy/core/make_example.py +0 -0
- {daspy_toolbox-1.2.1 → daspy_toolbox-1.2.3}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: DASPy-toolbox
|
|
3
|
-
Version: 1.2.
|
|
3
|
+
Version: 1.2.3
|
|
4
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
5
|
Home-page: https://github.com/HMZ-03/DASPy
|
|
6
6
|
Author: Minzhe Hu, Zefeng Li
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: DASPy-toolbox
|
|
3
|
-
Version: 1.2.
|
|
3
|
+
Version: 1.2.3
|
|
4
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
5
|
Home-page: https://github.com/HMZ-03/DASPy
|
|
6
6
|
Author: Minzhe Hu, Zefeng Li
|
|
@@ -181,7 +181,7 @@ def _channel_location(track_pt):
|
|
|
181
181
|
return np.array(seg_interval), np.array(interp_ch)
|
|
182
182
|
|
|
183
183
|
|
|
184
|
-
def location_interpolation(known_pt, track_pt=None, dx=
|
|
184
|
+
def location_interpolation(known_pt, track_pt=None, dx=1, data_type='lonlat',
|
|
185
185
|
verbose=False):
|
|
186
186
|
"""
|
|
187
187
|
Interpolate to obtain the positions of all channels.
|
|
@@ -209,7 +209,8 @@ def location_interpolation(known_pt, track_pt=None, dx=2, data_type='lonlat',
|
|
|
209
209
|
.astype(int) + 31
|
|
210
210
|
DASProj = Proj(proj='utm', zone=zone, ellps='WGS84',
|
|
211
211
|
preserve_units=False)
|
|
212
|
-
known_pt[:, 0], known_pt[:, 1] = DASProj(known_pt[:, 0],
|
|
212
|
+
known_pt[:, 0], known_pt[:, 1] = DASProj(known_pt[:, 0],
|
|
213
|
+
known_pt[:, 1])
|
|
213
214
|
else:
|
|
214
215
|
assert 'xy' in data_type, ('data_type should be \'lonlat\',\''
|
|
215
216
|
'lonlatheight\', \'xy\' or \'xyz\'')
|
|
@@ -23,14 +23,14 @@ def fk_rescaling(data, dx, fs, taper=(0.02, 0.05), pad='default', fmax=None,
|
|
|
23
23
|
:param dx: Channel interval in m.
|
|
24
24
|
:param fs: Sampling rate in Hz.
|
|
25
25
|
:param taper: float or sequence of floats. Each float means decimal
|
|
26
|
-
percentage of Tukey taper for corresponding dimension (ranging from 0
|
|
27
|
-
1). Default is 0.1 which tapers 5% from the beginning and 5% from
|
|
28
|
-
end.
|
|
29
|
-
:param pad: Pad the data or not. It can be float or sequence of floats.
|
|
30
|
-
float means padding percentage before FFT for corresponding
|
|
31
|
-
If set to 0.1 will pad 5% before the beginning and after the
|
|
32
|
-
'default' means pad both dimensions to next power of 2. None or
|
|
33
|
-
means don't pad data before or during Fast Fourier Transform.
|
|
26
|
+
percentage of Tukey taper for corresponding dimension (ranging from 0
|
|
27
|
+
to 1). Default is 0.1 which tapers 5% from the beginning and 5% from
|
|
28
|
+
the end.
|
|
29
|
+
:param pad: Pad the data or not. It can be float or sequence of floats.
|
|
30
|
+
Each float means padding percentage before FFT for corresponding
|
|
31
|
+
dimension. If set to 0.1 will pad 5% before the beginning and after the
|
|
32
|
+
end. 'default' means pad both dimensions to next power of 2. None or
|
|
33
|
+
False means don't pad data before or during Fast Fourier Transform.
|
|
34
34
|
:param fmax, kmin, vmax: float or or sequence of 2 floats. Sequence of 2
|
|
35
35
|
floats represents the start and end of taper. Setting these parameters
|
|
36
36
|
can reduce artifacts.
|
|
@@ -71,7 +71,8 @@ def fk_rescaling(data, dx, fs, taper=(0.02, 0.05), pad='default', fmax=None,
|
|
|
71
71
|
kk = np.tile(k, (len(f), 1)).T
|
|
72
72
|
vv = - np.divide(ff, kk, out=np.ones_like(ff) * 1e10, where=kk != 0)
|
|
73
73
|
|
|
74
|
-
mask = fk_fan_mask(f, k, fmax=fmax, kmin=kmin, vmax=vmax, edge=edge)
|
|
74
|
+
mask = fk_fan_mask(f, k, fmax=fmax, kmin=kmin, vmax=vmax, edge=edge) \
|
|
75
|
+
* vv
|
|
75
76
|
mask[kk == 0] = 0
|
|
76
77
|
|
|
77
78
|
data_vel = irfft2(ifftshift(fk * mask, axes=0)).real[:nch, :nt]
|
|
@@ -91,12 +92,12 @@ def curvelet_conversion(data, dx, fs, pad=0.3, scale_begin=2, nbscales=None,
|
|
|
91
92
|
:param data: numpy.ndarray. Data to convert.
|
|
92
93
|
:param dx: Channel interval in m.
|
|
93
94
|
:param fs: Sampling rate in Hz.
|
|
94
|
-
:param pad: float or sequence of floats. Each float means padding
|
|
95
|
-
before FFT for corresponding dimension. If set to 0.1 will
|
|
96
|
-
the beginning and after the end.
|
|
95
|
+
:param pad: float or sequence of floats. Each float means padding
|
|
96
|
+
percentage before FFT for corresponding dimension. If set to 0.1 will
|
|
97
|
+
pad 5% before the beginning and after the end.
|
|
97
98
|
:param scale_begin: int. The beginning scale to do conversion.
|
|
98
|
-
:param nbscales: int. Number of scales including the coarsest wavelet
|
|
99
|
-
Default set to ceil(log2(min(M,N)) - 3).
|
|
99
|
+
:param nbscales: int. Number of scales including the coarsest wavelet
|
|
100
|
+
level. Default set to ceil(log2(min(M,N)) - 3).
|
|
100
101
|
:param nbangles: int. Number of angles at the 2nd coarsest level,
|
|
101
102
|
minimum 8, must be a multiple of 4.
|
|
102
103
|
:param turning: Sequence of int. Channel number of turning points.
|
|
@@ -110,7 +111,8 @@ def curvelet_conversion(data, dx, fs, pad=0.3, scale_begin=2, nbscales=None,
|
|
|
110
111
|
data_vel[s:e] = curvelet_conversion(data[s:e], dx, fs, pad=pad,
|
|
111
112
|
scale_begin=scale_begin,
|
|
112
113
|
nbscales=nbscales,
|
|
113
|
-
nbangles=nbangles,
|
|
114
|
+
nbangles=nbangles,
|
|
115
|
+
turning=None)
|
|
114
116
|
else:
|
|
115
117
|
if pad is None or pad is False:
|
|
116
118
|
pad = 0
|
|
@@ -227,8 +229,8 @@ def slant_stacking(data, dx, fs, L=None, slm=0.01,
|
|
|
227
229
|
for (s, e) in zip(start_ch, end_ch):
|
|
228
230
|
channel_seg = [ch-s for ch in range(s,e) if ch in channel]
|
|
229
231
|
if len(channel_seg):
|
|
230
|
-
d_vel = slant_stacking(data[s:e], dx, fs, L=L, slm=slm,
|
|
231
|
-
frqlow=frqlow, frqhigh=frqhigh,
|
|
232
|
+
d_vel = slant_stacking(data[s:e], dx, fs, L=L, slm=slm,
|
|
233
|
+
sls=sls, frqlow=frqlow, frqhigh=frqhigh,
|
|
232
234
|
turning=None, channel=channel_seg)
|
|
233
235
|
data_vel = np.vstack((data_vel, d_vel))
|
|
234
236
|
else:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Purpose: Analyze frequency attribute and transform in frequency domain
|
|
2
2
|
# Author: Minzhe Hu
|
|
3
|
-
# Date:
|
|
3
|
+
# Date: 2025.11.12
|
|
4
4
|
# Email: hmz2018@mail.ustc.edu.cn
|
|
5
5
|
import numpy as np
|
|
6
6
|
from numpy.fft import rfft, rfft2, fftshift, fftfreq, rfftfreq
|
|
@@ -147,3 +147,13 @@ def fk_transform(data, dx, fs, taper=(0, 0.05), nfft='default'):
|
|
|
147
147
|
f = rfftfreq(nfft[1], d=1. / fs)
|
|
148
148
|
k = fftshift(fftfreq(nfft[0], d=dx))
|
|
149
149
|
return fk, f, k
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def power(data):
|
|
153
|
+
"""
|
|
154
|
+
Calculate the power of each channel.
|
|
155
|
+
|
|
156
|
+
:param data: numpy.ndarray. Data to calculate the power.
|
|
157
|
+
:return: numpy.ndarray. Power of each channel.
|
|
158
|
+
"""
|
|
159
|
+
return np.sqrt(np.mean(data ** 2, axis=-1))
|
|
@@ -19,10 +19,10 @@ def phase2strain(data, lam, e, n, gl):
|
|
|
19
19
|
:param e: float. photo-slastic scaling factor for logitudinal strain in
|
|
20
20
|
isotropic material.
|
|
21
21
|
:param n: float. Refractive index of the sensing fiber.
|
|
22
|
-
:paran
|
|
22
|
+
:paran guage_length: float. Gauge length.
|
|
23
23
|
:return: Strain data.
|
|
24
24
|
"""
|
|
25
|
-
return data *
|
|
25
|
+
return data * lam / (e * 4 * np.pi * n * gl)
|
|
26
26
|
|
|
27
27
|
|
|
28
28
|
def normalization(data, method='z-score'):
|
|
@@ -121,9 +121,9 @@ def cosine_taper(data, p=0.1, side='both'):
|
|
|
121
121
|
if not isinstance(p, (tuple, list, np.ndarray)):
|
|
122
122
|
win = tukey(nsp, p)
|
|
123
123
|
if side == 'left':
|
|
124
|
-
win[round(
|
|
124
|
+
win[round(nsp/2):] = 1
|
|
125
125
|
elif side == 'right':
|
|
126
|
-
win[:round(
|
|
126
|
+
win[:round(nsp/2)] = 1
|
|
127
127
|
return data * np.tile(win, (nch, 1))
|
|
128
128
|
else:
|
|
129
129
|
if p[0] > 0:
|
|
@@ -11,9 +11,9 @@ def plot(data: np.ndarray, dx=None, fs=None, ax=None, obj='waveform', dpi=300,
|
|
|
11
11
|
title=None, transpose=False, t0=0, x0=0, pick=None, f=None, k=None,
|
|
12
12
|
t=None, c=None, cmap=None, vmin=None, vmin_per=None, vmax=None,
|
|
13
13
|
vmax_per=None, dB=False, xmode='distance', tmode='time', xlim=None,
|
|
14
|
-
ylim=None, xlog=False, ylog=False, xinv=False, yinv=False,
|
|
15
|
-
|
|
16
|
-
colorbar_label=None, savefig=None):
|
|
14
|
+
ylim=None, xlog=False, ylog=False, xinv=False, yinv=False,
|
|
15
|
+
xlabel=True, ylabel=True, xticklabels=True, yticklabels=True,
|
|
16
|
+
colorbar=True, colorbar_label=None, savefig=None):
|
|
17
17
|
"""
|
|
18
18
|
Plot several types of 2-D seismological data.
|
|
19
19
|
|
|
@@ -42,19 +42,20 @@ def plot(data: np.ndarray, dx=None, fs=None, ax=None, obj='waveform', dpi=300,
|
|
|
42
42
|
:param vmin, vmax: Define the data range that the colormap covers.
|
|
43
43
|
:param vmin_per, vmax_per: float. Define the data range that the colormap
|
|
44
44
|
covers by percentile.
|
|
45
|
-
:param dB: bool. Transfer data unit to dB and take 1 as the reference
|
|
45
|
+
:param dB: bool. Transfer data unit to dB and take 1 as the reference
|
|
46
|
+
value.
|
|
46
47
|
:param xmode: str. 'distance' or 'channel'.
|
|
47
48
|
:param tmode: str. 'time' or 'sampling'.
|
|
48
49
|
:param xlim, ylim: Set the x-axis and y-axis view limits.
|
|
49
50
|
:param xlog, ylog: bool. If True, set the x-axis' or y-axis' scale as log.
|
|
50
51
|
:param xlabel, yinv: bool. If True, invert x-axis or y-axis.
|
|
51
|
-
:param xlabel, ylabel: bool or str. Whether to plot a label or what label
|
|
52
|
-
plot for x-axis or y-axis.
|
|
52
|
+
:param xlabel, ylabel: bool or str. Whether to plot a label or what label
|
|
53
|
+
to plot for x-axis or y-axis.
|
|
53
54
|
:param xticklabels, yticklabels: bool or sequence of str. Whether to plot
|
|
54
55
|
ticklabels or what ticklabels to plot for x-axis or y-axis.
|
|
55
|
-
:param colorbar: bool, str or Matplotlib.axes.Axes. Bool means plot
|
|
56
|
-
or not. Str means the location of colorbar. Axes means the
|
|
57
|
-
which the colorbar will be drawn.
|
|
56
|
+
:param colorbar: bool, str or Matplotlib.axes.Axes. Bool means plot
|
|
57
|
+
colorbar or not. Str means the location of colorbar. Axes means the
|
|
58
|
+
Axes into which the colorbar will be drawn.
|
|
58
59
|
:param savefig: str or bool. Figure name to save if needed. If True,
|
|
59
60
|
it will be set to parameter obj.
|
|
60
61
|
"""
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Purpose: Module for handling Collection objects.
|
|
2
2
|
# Author: Minzhe Hu
|
|
3
|
-
# Date: 2025.
|
|
3
|
+
# Date: 2025.11.26
|
|
4
4
|
# Email: hmz2018@mail.ustc.edu.cn
|
|
5
5
|
import os
|
|
6
6
|
import warnings
|
|
@@ -228,6 +228,8 @@ class Collection(object):
|
|
|
228
228
|
:param spmin, spmax: int. Sampling point range. Only used when
|
|
229
229
|
readsec=True.
|
|
230
230
|
"""
|
|
231
|
+
if start is None:
|
|
232
|
+
start = 0
|
|
231
233
|
if end is None:
|
|
232
234
|
end = len(self.flist)
|
|
233
235
|
if 'stime' in kwargs.keys():
|
|
@@ -239,10 +241,16 @@ class Collection(object):
|
|
|
239
241
|
warnings.warn('In future versions, the parameter \'etime\' will be '
|
|
240
242
|
'replaced by \'end\'.')
|
|
241
243
|
|
|
242
|
-
if start is None
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
244
|
+
if start is None:
|
|
245
|
+
if 'tmin' in kwargs.keys():
|
|
246
|
+
start = kwargs['tmin']
|
|
247
|
+
else:
|
|
248
|
+
start = 0
|
|
249
|
+
if end is None:
|
|
250
|
+
if 'tmax' in kwargs.keys():
|
|
251
|
+
end = kwargs['tmax']
|
|
252
|
+
else:
|
|
253
|
+
end = len(self)
|
|
246
254
|
|
|
247
255
|
if isinstance(start, datetime):
|
|
248
256
|
for i, ftime in enumerate(self.ftime):
|
|
@@ -288,6 +296,18 @@ class Collection(object):
|
|
|
288
296
|
self.ftime = self.ftime[s:e]
|
|
289
297
|
return self
|
|
290
298
|
|
|
299
|
+
def read(self, **kwargs):
|
|
300
|
+
return self.select(readsec=True, **kwargs)
|
|
301
|
+
|
|
302
|
+
def continuous_acquisition(self):
|
|
303
|
+
index = self.file_interruption()
|
|
304
|
+
index = [-1] + index.tolist() + [len(self)-1]
|
|
305
|
+
coll_list = []
|
|
306
|
+
for i in range(len(index) - 1):
|
|
307
|
+
coll = self.copy().select(start=index[i]+1, end=index[i+1]+1)
|
|
308
|
+
coll_list.append(coll)
|
|
309
|
+
return coll_list
|
|
310
|
+
|
|
291
311
|
def _optimize_for_continuity(self, operations):
|
|
292
312
|
method_list = []
|
|
293
313
|
kwargs_list = []
|
|
@@ -296,8 +316,10 @@ class Collection(object):
|
|
|
296
316
|
for opera in operations:
|
|
297
317
|
method, kwargs = opera
|
|
298
318
|
if method == 'downsampling':
|
|
299
|
-
|
|
300
|
-
kwargs
|
|
319
|
+
if_filter = ('tint' not in kwargs.keys() and 'fs' not in
|
|
320
|
+
kwargs.keys()) or ('lowpass_filter' in kwargs.keys() and
|
|
321
|
+
not kwargs['lowpass_filter'])
|
|
322
|
+
if if_filter:
|
|
301
323
|
method_list.append('downsampling')
|
|
302
324
|
kwargs_list.append(kwargs)
|
|
303
325
|
else:
|
|
@@ -356,9 +378,9 @@ class Collection(object):
|
|
|
356
378
|
(not os.path.exists(kwargs_file)):
|
|
357
379
|
raise ValueError('No operations input and no method_list.pkl '
|
|
358
380
|
'and kwargs_list.pkl found in savepath.')
|
|
359
|
-
with open(os.path.join(savepath, 'method_list.pkl'), '
|
|
381
|
+
with open(os.path.join(savepath, 'method_list.pkl'), 'rb') as f:
|
|
360
382
|
method_list = pickle.load(f)
|
|
361
|
-
with open(os.path.join(savepath, 'kwargs_list.pkl'), '
|
|
383
|
+
with open(os.path.join(savepath, 'kwargs_list.pkl'), 'rb') as f:
|
|
362
384
|
kwargs_list = pickle.load(f)
|
|
363
385
|
else:
|
|
364
386
|
method_list, kwargs_list = self._optimize_for_continuity(operations)
|
|
@@ -9,7 +9,7 @@ from datetime import datetime, timedelta, timezone
|
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
utc = timezone.utc
|
|
12
|
-
local_tz = timezone(timedelta(seconds=-time.
|
|
12
|
+
local_tz = timezone(timedelta(seconds=-time.timezone))
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
class DASDateTime(datetime):
|
|
@@ -69,7 +69,7 @@ class DASDateTime(datetime):
|
|
|
69
69
|
|
|
70
70
|
@classmethod
|
|
71
71
|
def from_obspy_UTCDateTime(cls, dt):
|
|
72
|
-
return cls.from_datetime(dt.datetime)
|
|
72
|
+
return cls.from_datetime(dt.datetime).replace(tzinfo=utc)
|
|
73
73
|
|
|
74
74
|
def to_datetime(self):
|
|
75
75
|
return datetime.fromtimestamp(self.timestamp(), tz=self.tzinfo)
|
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
# Purpose: Module for reading DAS data.
|
|
2
|
-
# Author: Minzhe Hu
|
|
3
|
-
# Date: 2025.
|
|
2
|
+
# Author: Minzhe Hu, Ji Zhang
|
|
3
|
+
# Date: 2025.11.19
|
|
4
4
|
# Email: hmz2018@mail.ustc.edu.cn
|
|
5
5
|
# Partially modified from
|
|
6
6
|
# https://github.com/RobbinLuo/das-toolkit/blob/main/DasTools/DasPrep.py
|
|
7
7
|
import warnings
|
|
8
|
+
import inspect
|
|
8
9
|
import json
|
|
9
10
|
import pickle
|
|
10
11
|
import numpy as np
|
|
11
12
|
import h5py
|
|
12
13
|
import segyio
|
|
14
|
+
from functools import wraps
|
|
13
15
|
from typing import Union
|
|
14
16
|
from pathlib import Path
|
|
15
17
|
from nptdms import TdmsFile
|
|
@@ -73,13 +75,8 @@ def read(fname=None, output_type='section', ftype=None, file_format='auto',
|
|
|
73
75
|
kwargs['chmin'] = kwargs.pop('ch1', None)
|
|
74
76
|
kwargs['chmax'] = kwargs.pop('ch2', None)
|
|
75
77
|
if callable(ftype):
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
except TypeError:
|
|
79
|
-
data, metadata = ftype(fname)
|
|
80
|
-
si, sj, metadata = _trimming_slice_metadata(data.shape,
|
|
81
|
-
metadata=metadata, **kwargs)
|
|
82
|
-
data = data[si, sj]
|
|
78
|
+
ftype = with_trimming(ftype)
|
|
79
|
+
data, metadata = ftype(fname, headonly=headonly, **kwargs)
|
|
83
80
|
else:
|
|
84
81
|
for rtp in [('pickle', 'pkl'), ('hdf5', 'h5'), ('segy', 'sgy')]:
|
|
85
82
|
ftype = ftype.replace(*rtp)
|
|
@@ -97,6 +94,60 @@ def read(fname=None, output_type='section', ftype=None, file_format='auto',
|
|
|
97
94
|
return data, metadata
|
|
98
95
|
|
|
99
96
|
|
|
97
|
+
def with_trimming(func):
|
|
98
|
+
"""
|
|
99
|
+
Decorator that wraps a custom reader so it automatically supports
|
|
100
|
+
trimming parameters (chmin, chmax, dch, xmin, xmax, tmin, tmax, spmin, spmax).
|
|
101
|
+
"""
|
|
102
|
+
|
|
103
|
+
@wraps(func)
|
|
104
|
+
def wrapper(fname, headonly=False, **kwargs):
|
|
105
|
+
# trimming-related parameters
|
|
106
|
+
trim_keys = ['chmin', 'chmax', 'dch', 'xmin', 'xmax', 'tmin', 'tmax',
|
|
107
|
+
'spmin', 'spmax']
|
|
108
|
+
sig = inspect.signature(func)
|
|
109
|
+
reader_params = set(sig.parameters.keys())
|
|
110
|
+
trim_for_reader = {k: kwargs.pop(k) for k in trim_keys if k in kwargs \
|
|
111
|
+
and k in reader_params}
|
|
112
|
+
trim_for_trimming = {k: kwargs.pop(k) for k in trim_keys if k in \
|
|
113
|
+
kwargs and k not in reader_params}
|
|
114
|
+
try:
|
|
115
|
+
data, metadata = func(fname, headonly=headonly, **trim_for_reader,
|
|
116
|
+
**kwargs)
|
|
117
|
+
except TypeError:
|
|
118
|
+
headonly = False
|
|
119
|
+
data, metadata = func(fname, **trim_for_reader)
|
|
120
|
+
|
|
121
|
+
shape = data.shape
|
|
122
|
+
si, sj, metadata = _trimming_slice_metadata(shape, metadata=metadata,
|
|
123
|
+
**trim_for_trimming)
|
|
124
|
+
if headonly:
|
|
125
|
+
data = np.zeros(shape)[si, sj]
|
|
126
|
+
else:
|
|
127
|
+
data = data[si, sj]
|
|
128
|
+
|
|
129
|
+
return data, metadata
|
|
130
|
+
|
|
131
|
+
return wrapper
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
class DummyObject:
|
|
135
|
+
def __init__(self, *args, **kwargs):
|
|
136
|
+
pass
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
class SafeUnpickler(pickle.Unpickler):
|
|
140
|
+
def find_class(self, module, name):
|
|
141
|
+
try:
|
|
142
|
+
return super().find_class(module, name)
|
|
143
|
+
except ModuleNotFoundError:
|
|
144
|
+
print(f"Skip missing module: {module}.{name}")
|
|
145
|
+
return DummyObject
|
|
146
|
+
except AttributeError:
|
|
147
|
+
print(f"Skip missing class: {module}.{name}")
|
|
148
|
+
return DummyObject
|
|
149
|
+
|
|
150
|
+
|
|
100
151
|
def _read_pkl(fname, headonly=False, file_format='auto', chmin=None, chmax=None,
|
|
101
152
|
dch=1, xmin=None, xmax=None, tmin=None, tmax=None, spmin=None,
|
|
102
153
|
spmax=None):
|
|
@@ -104,7 +155,8 @@ def _read_pkl(fname, headonly=False, file_format='auto', chmin=None, chmax=None,
|
|
|
104
155
|
Read data and metadata from a pickle file.
|
|
105
156
|
"""
|
|
106
157
|
with open(fname, 'rb') as f:
|
|
107
|
-
pkl_data = pickle.load(f)
|
|
158
|
+
# pkl_data = pickle.load(f)
|
|
159
|
+
pkl_data = SafeUnpickler(f).load()
|
|
108
160
|
if isinstance(pkl_data, np.ndarray):
|
|
109
161
|
warnings.warn('This data format doesn\'t include channel interval'
|
|
110
162
|
'and sampling rate. Please set manually')
|
|
@@ -290,6 +342,34 @@ def _read_h5(fname, headonly=False, file_format='auto', chmin=None, chmax=None,
|
|
|
290
342
|
'start_time': stime,
|
|
291
343
|
'gauge_length': attrs['GaugeLength']}
|
|
292
344
|
|
|
345
|
+
elif file_format == 'Puniu Tech HiFi-DAS':
|
|
346
|
+
dataset = h5_file['default']
|
|
347
|
+
if 'time,channel' in attrs.get('row_major_order', 'time, channel')\
|
|
348
|
+
.replace(' ', '').lower():
|
|
349
|
+
transpose = True
|
|
350
|
+
|
|
351
|
+
attrs = {k: (v.decode() if isinstance(v, bytes) else v) for k, v
|
|
352
|
+
in dataset.attrs.items()}
|
|
353
|
+
step = int(attrs['step'])
|
|
354
|
+
dx = step * attrs.get('spatial_sampling_rate', 1.0)
|
|
355
|
+
start_channel = int(attrs['start_channel'])
|
|
356
|
+
if step != 1:
|
|
357
|
+
if chmin:
|
|
358
|
+
chmin = (chmin - start_channel) / step + start_channel
|
|
359
|
+
if chmax:
|
|
360
|
+
chmax = (chmin - start_channel) / step + start_channel
|
|
361
|
+
t0 = int(attrs.get('epoch', 0)) + int(attrs.get('ns', 0)) * 1e-9
|
|
362
|
+
data_type = 'strain rate' if attrs.get('format', '') == \
|
|
363
|
+
'differential' else 'strain',
|
|
364
|
+
metadata = {'dx': dx, 'fs': float(attrs['sampling_rate']),
|
|
365
|
+
'start_channel': start_channel,
|
|
366
|
+
'start_distance': start_channel * dx,
|
|
367
|
+
'start_time': DASDateTime.fromtimestamp(t0, tz=utc),
|
|
368
|
+
'scale': 110.37, 'data_type': data_type,
|
|
369
|
+
'cid': attrs.get('cid', '')}
|
|
370
|
+
if 'spatial_resolution' in attrs.keys():
|
|
371
|
+
metadata['gauge_length'] = float(attrs['spatial_resolution'])
|
|
372
|
+
|
|
293
373
|
elif file_format == 'Silixa iDAS':
|
|
294
374
|
dataset = h5_file['Acquisition/Raw[0]/RawData/']
|
|
295
375
|
attrs = h5_file['Acquisition/Raw[0]'].attrs
|
|
@@ -356,13 +436,36 @@ def _read_h5(fname, headonly=False, file_format='auto', chmin=None, chmax=None,
|
|
|
356
436
|
metadata = {'dx': h5_file['Sampling_interval_in_space'][0],
|
|
357
437
|
'fs': 1 / h5_file['Sampling_interval_in_time'][0]}
|
|
358
438
|
|
|
439
|
+
elif file_format == 'NEC':
|
|
440
|
+
dataset = h5_file['data']
|
|
441
|
+
dx = dataset.attrs['Interval of monitor point']
|
|
442
|
+
fs = 1.0 / (dataset.attrs['Interval time of data'] / 1000.0) # Hz
|
|
443
|
+
if dataset.shape[0] != \
|
|
444
|
+
dataset.attrs['Number of requested location points']:
|
|
445
|
+
transpose = True
|
|
446
|
+
try:
|
|
447
|
+
scale = dataset.attrs['Radians peer digital value']
|
|
448
|
+
except KeyError:
|
|
449
|
+
try:
|
|
450
|
+
scale = dataset.attrs['Radians per digital value']
|
|
451
|
+
except KeyError:
|
|
452
|
+
scale = 1
|
|
453
|
+
# start_time = datetime(1970, 1, 1) + \
|
|
454
|
+
# timedelta(milliseconds=start_unix_epoch_in_ms)
|
|
455
|
+
start_time = DASDateTime.fromtimestamp(
|
|
456
|
+
np.float64(dataset.attrs['Time of sending request']) / 1e3
|
|
457
|
+
).utc()
|
|
458
|
+
metadata = {'fs': fs, 'dx': dx, 'start_time': start_time,
|
|
459
|
+
'gauge_length': dataset.attrs['Gauge length'],
|
|
460
|
+
'scale': scale, 'data_type':'strain rate'}
|
|
461
|
+
|
|
359
462
|
elif file_format == 'FORESEE':
|
|
360
463
|
dataset = h5_file['raw']
|
|
361
464
|
fs = round(1 / np.diff(h5_file['timestamp']).mean())
|
|
362
465
|
start_time = DASDateTime.fromtimestamp(
|
|
363
466
|
h5_file['timestamp'][0]).astimezone(utc)
|
|
364
|
-
warnings.warn('This data format doesn\'t include channel interval.
|
|
365
|
-
|
|
467
|
+
warnings.warn('This data format doesn\'t include channel interval.'
|
|
468
|
+
' Please set manually')
|
|
366
469
|
metadata = {'dx': None, 'fs': fs, 'start_time': start_time}
|
|
367
470
|
|
|
368
471
|
elif file_format == 'AI4EPS': # https://ai4eps.github.io/homepage/ml4earth/seismic_event_format_das/
|
|
@@ -399,7 +502,13 @@ def _read_h5(fname, headonly=False, file_format='auto', chmin=None, chmax=None,
|
|
|
399
502
|
metadata['headers'] = _read_h5_headers(h5_file)
|
|
400
503
|
shape = dataset.shape
|
|
401
504
|
if len(shape) == 3:
|
|
402
|
-
|
|
505
|
+
if headonly:
|
|
506
|
+
fs = int(metadata['fs'])
|
|
507
|
+
fs_b = attrs.get('BlockRate', [1000])[0] / 1e3
|
|
508
|
+
nsp_b = round(fs/fs_b)
|
|
509
|
+
shape = (shape[0] * nsp_b, shape[2])
|
|
510
|
+
else:
|
|
511
|
+
shape = (shape[0] * shape[1], shape[2])
|
|
403
512
|
if transpose:
|
|
404
513
|
shape = shape[::-1]
|
|
405
514
|
si, sj, metadata = _trimming_slice_metadata(shape, metadata=metadata,
|
|
@@ -500,7 +609,6 @@ def _read_tdms(fname, headonly=False, file_format='auto', chmin=None,
|
|
|
500
609
|
data = np.asarray([tdms_file[key][str(ch)][sj] for ch in
|
|
501
610
|
range(si.start, si.stop, si.step)])
|
|
502
611
|
elif file_format == 'Institute of Semiconductors, CAS':
|
|
503
|
-
|
|
504
612
|
try:
|
|
505
613
|
start_channel = int(properties['Initial Channel'])
|
|
506
614
|
except KeyError:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Purpose: Module for handling Section objects.
|
|
2
2
|
# Author: Minzhe Hu
|
|
3
|
-
# Date: 2025.
|
|
3
|
+
# Date: 2025.11.19
|
|
4
4
|
# Email: hmz2018@mail.ustc.edu.cn
|
|
5
5
|
import warnings
|
|
6
6
|
import os
|
|
@@ -21,7 +21,7 @@ from daspy.basic_tools.preprocessing import (phase2strain, normalization,
|
|
|
21
21
|
from daspy.basic_tools.filter import (bandpass, bandstop, lowpass,
|
|
22
22
|
lowpass_cheby_2, highpass, envelope)
|
|
23
23
|
from daspy.basic_tools.freqattributes import (spectrum, spectrogram, psd,
|
|
24
|
-
fk_transform)
|
|
24
|
+
fk_transform, power)
|
|
25
25
|
from daspy.advanced_tools.channel import channel_checking, turning_points
|
|
26
26
|
from daspy.advanced_tools.denoising import (curvelet_denoising,
|
|
27
27
|
common_mode_noise_removal,
|
|
@@ -68,6 +68,7 @@ class Section(object):
|
|
|
68
68
|
opt_attrs = ['origin_time', 'gauge_length', 'data_type', 'scale',
|
|
69
69
|
'geometry', 'turning_channels', 'headers', 'source',
|
|
70
70
|
'source_type', 'file_format']
|
|
71
|
+
kwargs.setdefault('scale', 1)
|
|
71
72
|
for attr in opt_attrs:
|
|
72
73
|
if attr in kwargs:
|
|
73
74
|
setattr(self, attr, kwargs.pop(attr))
|
|
@@ -195,7 +196,7 @@ class Section(object):
|
|
|
195
196
|
|
|
196
197
|
@property
|
|
197
198
|
def end_channel(self):
|
|
198
|
-
return self.start_channel + self.nch
|
|
199
|
+
return self.start_channel + self.nch
|
|
199
200
|
|
|
200
201
|
@property
|
|
201
202
|
def distance(self):
|
|
@@ -462,8 +463,8 @@ class Section(object):
|
|
|
462
463
|
:param ftype: None or str. None for automatic detection), or 'pkl',
|
|
463
464
|
'pickle', 'tdms', 'h5', 'hdf5', 'segy', 'sgy', 'npy'.
|
|
464
465
|
:param keep_format: bool. If True, we will make a copy of the
|
|
465
|
-
self.source file and make changes to it. This will strictly
|
|
466
|
-
the original format, but will cost more IO resources.
|
|
466
|
+
self.source file and make changes to it. This will strictly
|
|
467
|
+
preserve the original format, but will cost more IO resources.
|
|
467
468
|
:param dtype: str. The data type of the saved data.
|
|
468
469
|
:param file_format: Format in which the file is saved. Only works when
|
|
469
470
|
keep_format == False. 'auto' for raw file format or the most common
|
|
@@ -646,9 +647,11 @@ class Section(object):
|
|
|
646
647
|
if hasattr(self, 'origin_time'):
|
|
647
648
|
kwargs['t0'] -= self.origin_time
|
|
648
649
|
if ('transpose' in kwargs.keys()) and kwargs['transpose']:
|
|
649
|
-
kwargs.setdefault('xlabel',
|
|
650
|
+
kwargs.setdefault('xlabel',
|
|
651
|
+
'Times after occurance (s)')
|
|
650
652
|
else:
|
|
651
|
-
kwargs.setdefault('ylabel',
|
|
653
|
+
kwargs.setdefault('ylabel',
|
|
654
|
+
'Times after occurance (s)')
|
|
652
655
|
else:
|
|
653
656
|
tmode == 'start'
|
|
654
657
|
if tmode == 'start':
|
|
@@ -1096,12 +1099,13 @@ class Section(object):
|
|
|
1096
1099
|
if ('ch1' in kwargs.keys()) or ('ch2' in kwargs.keys()):
|
|
1097
1100
|
kwargs['chmin'] = kwargs.pop('ch1', 0)
|
|
1098
1101
|
kwargs['chmax'] = kwargs.pop('ch2', self.nch)
|
|
1099
|
-
warnings.warn("'ch1' and 'ch2' attribute will be renamed to
|
|
1100
|
-
" and 'chmax' in a future release.",
|
|
1102
|
+
warnings.warn("'ch1' and 'ch2' attribute will be renamed to "
|
|
1103
|
+
"'chmin' and 'chmax' in a future release.",
|
|
1104
|
+
FutureWarning)
|
|
1101
1105
|
if 'nch' in kwargs.keys():
|
|
1102
1106
|
kwargs['dch'] = kwargs.pop('nch', 1)
|
|
1103
|
-
warnings.warn("'nch' attribute will be renamed to 'dch' in a
|
|
1104
|
-
" release.", FutureWarning)
|
|
1107
|
+
warnings.warn("'nch' attribute will be renamed to 'dch' in a "
|
|
1108
|
+
"future release.", FutureWarning)
|
|
1105
1109
|
|
|
1106
1110
|
if 'chmin' in kwargs.keys():
|
|
1107
1111
|
chmin = int(kwargs.pop('chmin') - self.start_channel)
|
|
@@ -1132,6 +1136,14 @@ class Section(object):
|
|
|
1132
1136
|
"""
|
|
1133
1137
|
return fk_transform(self.data, self.dx, self.fs, **kwargs)
|
|
1134
1138
|
|
|
1139
|
+
def power(self):
|
|
1140
|
+
"""
|
|
1141
|
+
Calculate the power of each channel.
|
|
1142
|
+
|
|
1143
|
+
:return: numpy.ndarray. Power of each channel.
|
|
1144
|
+
"""
|
|
1145
|
+
return power(self.data)
|
|
1146
|
+
|
|
1135
1147
|
def channel_checking(self, use=False, **kwargs):
|
|
1136
1148
|
"""
|
|
1137
1149
|
Use the energy of each channel to determine which channels are bad.
|
|
@@ -21,13 +21,16 @@ def _device_standardized_name(file_format: str) -> str:
|
|
|
21
21
|
'OptaSense ODH4+': ['optasenseodh4+', 'odh4+', 'optasenseodh4plus',
|
|
22
22
|
'odh4plus'],
|
|
23
23
|
'OptaSense QuantX': ['optasensequantx', 'quantx'],
|
|
24
|
+
'Puniu Tech HiFi-DAS': ['puniutechhifidas, puniu, puniutech, hifidas',
|
|
25
|
+
'puniuhifidas', 'puniudas'],
|
|
24
26
|
'Silixa iDAS': ['silixaidas', 'silixaidasv1', 'idasv1', 'idas'],
|
|
25
27
|
'Silixa iDAS-v2': ['silixaidasv2', 'idasv2'],
|
|
26
28
|
'Silixa iDAS-v3': ['silixaidasv3', 'idasv3'],
|
|
27
29
|
'Silixa iDAS-MG': ['silixaidasmg', 'idasmg'],
|
|
28
30
|
'Silixa Carina': ['silixacarina', 'carina'],
|
|
29
|
-
'Sintela Onyx v1.0': ['sintelaonyxv1.0', 'sintelaonyxv1',
|
|
30
|
-
'sintela', 'onyxv1.0', 'onyxv1',
|
|
31
|
+
'Sintela Onyx v1.0': ['sintelaonyxv1.0', 'sintelaonyxv1',
|
|
32
|
+
'sintalaonyx', 'sintela', 'onyxv1.0', 'onyxv1',
|
|
33
|
+
'onyx'],
|
|
31
34
|
'T8 Sensor': ['t8sensor', 't8'],
|
|
32
35
|
'Smart Earth ZD-DAS': ['smartearthzddas', 'smartearth', 'zddas',
|
|
33
36
|
'smartearthsensingzddas', 'smartearthsensing',
|
|
@@ -37,7 +40,9 @@ def _device_standardized_name(file_format: str) -> str:
|
|
|
37
40
|
'instituteofsemiconductorscas'],
|
|
38
41
|
'AI4EPS': ['ai4eps', 'daseventdata'],
|
|
39
42
|
'INGV': ['ingv', 'istitutonazionaledigeofisicaevulcanologia'],
|
|
40
|
-
'JAMSTEC': ['jamstec',
|
|
43
|
+
'JAMSTEC': ['jamstec',
|
|
44
|
+
'japanagencyformarineearthscienceandtechnology'],
|
|
45
|
+
'NEC': ['nec', 'nipponelectriccompany'],
|
|
41
46
|
'FORESEE': ['forsee', 'fiberopticforenvironmentsenseing'],
|
|
42
47
|
'Unknown0': ['unknown0'],
|
|
43
48
|
'Unknown': ['unknown', 'other']
|
|
@@ -88,16 +93,22 @@ def _h5_file_format(h5_file):
|
|
|
88
93
|
file_format = 'Sintela Onyx v1.0'
|
|
89
94
|
except KeyError:
|
|
90
95
|
pass
|
|
96
|
+
elif 'default' in keys:
|
|
97
|
+
file_format = 'Puniu Tech HiFi-DAS'
|
|
91
98
|
elif set(keys) == {'Mapping', 'Acquisition'}:
|
|
92
99
|
file_format = 'Silixa iDAS'
|
|
93
|
-
elif list(keys) == ['data']:
|
|
94
|
-
file_format == 'AI4EPS'
|
|
95
100
|
elif set(keys) == {'ChannelMap', 'Fiber', 'cm', 't', 'x'}:
|
|
96
101
|
file_format = 'INGV'
|
|
97
102
|
elif set(keys) == {'DAS_record', 'Sampling_interval_in_space',
|
|
98
103
|
'Sampling_interval_in_time', 'Sampling_points_in_space',
|
|
99
104
|
'Sampling_points_in_time'}:
|
|
100
105
|
file_format = 'JAMSTEC'
|
|
106
|
+
elif list(keys) == ['data']:
|
|
107
|
+
if 'Interval of monitor point' in \
|
|
108
|
+
list(h5_file['data'].attrs['Interval of monitor point'].keys()):
|
|
109
|
+
file_format = 'NEC'
|
|
110
|
+
else:
|
|
111
|
+
file_format = 'AI4EPS'
|
|
101
112
|
elif set(keys) == {'raw', 'timestamp'}:
|
|
102
113
|
file_format = 'FORESEE'
|
|
103
114
|
elif list(keys) == ['ProcessedData']:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Purpose: Module for writing DAS data.
|
|
2
2
|
# Author: Minzhe Hu
|
|
3
|
-
# Date: 2025.
|
|
3
|
+
# Date: 2025.11.19
|
|
4
4
|
# Email: hmz2018@mail.ustc.edu.cn
|
|
5
5
|
import os
|
|
6
6
|
import warnings
|
|
@@ -35,8 +35,10 @@ def write(sec, fname, ftype=None, raw_fname=None, dtype=None,
|
|
|
35
35
|
|
|
36
36
|
|
|
37
37
|
def write_pkl(sec, fname):
|
|
38
|
+
save_dict = sec.__dict__
|
|
39
|
+
save_dict.pop('source', None)
|
|
38
40
|
with open(fname, 'wb') as f:
|
|
39
|
-
pickle.dump(
|
|
41
|
+
pickle.dump(save_dict, f)
|
|
40
42
|
return None
|
|
41
43
|
|
|
42
44
|
|
|
@@ -192,9 +194,39 @@ def _write_h5(sec, fname, raw_fname=None, file_format='auto'):
|
|
|
192
194
|
h5_file.get('Acquisition/Raw[0]/').\
|
|
193
195
|
create_dataset('RawDataTime', data=datatime)
|
|
194
196
|
h5_file['Acquisition/Raw[0]'].attrs['OutputDataRate'] = sec.fs
|
|
195
|
-
h5_file['Acquisition'].attrs['SpatialSamplingInterval'] =
|
|
196
|
-
|
|
197
|
-
|
|
197
|
+
h5_file['Acquisition'].attrs['SpatialSamplingInterval'] = \
|
|
198
|
+
sec.dx
|
|
199
|
+
h5_file['Acquisition'].attrs['GaugeLength'] = \
|
|
200
|
+
sec.gauge_length if hasattr(sec, 'gauge_length') else -1
|
|
201
|
+
|
|
202
|
+
elif file_format == 'Puniu Tech HiFi-DAS':
|
|
203
|
+
h5_file.create_dataset('default', data=sec.data)
|
|
204
|
+
h5_file['default'].attrs['row_major_order'] = 'channel, time'
|
|
205
|
+
h5_file['default'].attrs['spatial_sampling_rate'] = sec.dx
|
|
206
|
+
h5_file['default'].attrs['step'] = 1
|
|
207
|
+
h5_file['default'].attrs['sampling_rate'] = sec.fs
|
|
208
|
+
h5_file['default'].attrs['start_channel'] = sec.start_channel
|
|
209
|
+
if isinstance(sec.start_time, datetime):
|
|
210
|
+
t0 = sec.start_time.timestamp()
|
|
211
|
+
else:
|
|
212
|
+
t0 = sec.start_time
|
|
213
|
+
epoch = int(t0)
|
|
214
|
+
h5_file['default'].attrs['epoch'] = epoch
|
|
215
|
+
h5_file['default'].attrs['ns'] = int(round((t0 - epoch) * 1e9))
|
|
216
|
+
if hasattr(sec, 'data_type'):
|
|
217
|
+
h5_file['default'].attrs['format'] = 'differential' if \
|
|
218
|
+
sec.data_type == 'strain rate' else sec.data_type
|
|
219
|
+
else:
|
|
220
|
+
h5_file['default'].attrs['format'] = 'unknown'
|
|
221
|
+
|
|
222
|
+
if hasattr(sec, 'gauge_length'):
|
|
223
|
+
h5_file['default'].attrs['spatial_resolution'] = \
|
|
224
|
+
sec.gauge_length
|
|
225
|
+
try:
|
|
226
|
+
h5_file['default'].attrs['cid'] = \
|
|
227
|
+
sec.headers['default']['attrs']['cid']
|
|
228
|
+
except KeyError:
|
|
229
|
+
h5_file['default'].attrs['cid'] = 'unknown'
|
|
198
230
|
|
|
199
231
|
elif file_format == 'Silixa iDAS':
|
|
200
232
|
h5_file.create_group('Acquisition/Raw[0]')
|
|
@@ -273,7 +305,6 @@ def _write_h5(sec, fname, raw_fname=None, file_format='auto'):
|
|
|
273
305
|
h5_file.attrs['FilterGain'] = 116e-9 * sec.fs / gauge_length / \
|
|
274
306
|
8192 / scale
|
|
275
307
|
|
|
276
|
-
|
|
277
308
|
elif file_format in 'JAMSTEC':
|
|
278
309
|
h5_file.create_dataset('DAS_record', data=sec.data)
|
|
279
310
|
h5_file.create_dataset('Sampling_interval_in_space',
|
|
@@ -281,6 +312,23 @@ def _write_h5(sec, fname, raw_fname=None, file_format='auto'):
|
|
|
281
312
|
h5_file.create_dataset('Sampling_interval_in_time',
|
|
282
313
|
data=[1/sec.fs])
|
|
283
314
|
|
|
315
|
+
elif file_format == 'NEC':
|
|
316
|
+
h5_file.create_dataset('data', data=sec.data)
|
|
317
|
+
h5_file['data'].attrs['Interval of monitor point'] = sec.dx
|
|
318
|
+
h5_file['data'].attrs['Number of requested location points'] \
|
|
319
|
+
= sec.nch
|
|
320
|
+
h5_file['data'].attrs['Interval time of data'] = \
|
|
321
|
+
1 / sec.fs * 1e3
|
|
322
|
+
h5_file['data'].attrs['Radians per digital value'] = \
|
|
323
|
+
sec.scale if hasattr(sec, 'scale') else 1
|
|
324
|
+
if isinstance(sec.start_time, datatime):
|
|
325
|
+
t0 = sec.start_time.timestamp()
|
|
326
|
+
else:
|
|
327
|
+
t0 = sec.start_time
|
|
328
|
+
h5_file['data'].attrs['Time of sending request'] = t0 * 1e3
|
|
329
|
+
h5_file['data'].attrs['Gauge length'] = sec.gauge_length if \
|
|
330
|
+
hasattr(sec, 'gauge_length') else -1
|
|
331
|
+
|
|
284
332
|
elif file_format == 'FORESEE':
|
|
285
333
|
h5_file.create_dataset('raw', data=sec.data)
|
|
286
334
|
start_time = sec.start_time.timestamp() if \
|
|
@@ -463,9 +511,42 @@ def _write_h5(sec, fname, raw_fname=None, file_format='auto'):
|
|
|
463
511
|
_update_h5_dataset(h5_file, 'Acquisition/Raw[0]/',
|
|
464
512
|
'RawDataTime', DataTime)
|
|
465
513
|
h5_file['Acquisition/Raw[0]'].attrs['OutputDataRate'] = sec.fs
|
|
466
|
-
h5_file['Acquisition'].attrs['SpatialSamplingInterval'] =
|
|
467
|
-
|
|
468
|
-
|
|
514
|
+
h5_file['Acquisition'].attrs['SpatialSamplingInterval'] = \
|
|
515
|
+
sec.dx
|
|
516
|
+
h5_file['Acquisition'].attrs['GaugeLength'] = \
|
|
517
|
+
sec.gauge_length if hasattr(sec, 'gauge_length') else -1
|
|
518
|
+
|
|
519
|
+
elif file_format == 'Puniu Tech HiFi-DAS':
|
|
520
|
+
attrs = {k: (v.decode() if isinstance(v, bytes) else v) for k,
|
|
521
|
+
v in h5_file['default'].attrs.items()}
|
|
522
|
+
if 'time,channel' in attrs.get('row_major_order',
|
|
523
|
+
'time, channel').replace(' ', '').lower():
|
|
524
|
+
_update_h5_dataset(h5_file, '/', 'default', sec.data.T)
|
|
525
|
+
else:
|
|
526
|
+
_update_h5_dataset(h5_file, '/', 'default', sec.data)
|
|
527
|
+
|
|
528
|
+
h5_file['default'].attrs['spatial_sampling_rate'] = sec.dx / \
|
|
529
|
+
attrs['step']
|
|
530
|
+
h5_file['default'].attrs['sampling_rate'] = sec.fs
|
|
531
|
+
h5_file['default'].attrs['start_channel'] = sec.start_channel
|
|
532
|
+
if isinstance(sec.start_time, datetime):
|
|
533
|
+
t0 = sec.start_time.timestamp()
|
|
534
|
+
else:
|
|
535
|
+
t0 = sec.start_time
|
|
536
|
+
epoch = int(t0)
|
|
537
|
+
h5_file['default'].attrs['epoch'] = epoch
|
|
538
|
+
h5_file['default'].attrs['ns'] = int(round((t0 - epoch) * 1e9))
|
|
539
|
+
if hasattr(sec, 'data_type'):
|
|
540
|
+
h5_file['default'].attrs['format'] = 'differential' if \
|
|
541
|
+
sec.data_type == 'strain rate' else sec.data_type
|
|
542
|
+
if hasattr(sec, 'gauge_length'):
|
|
543
|
+
h5_file['default'].attrs['spatial_resolution'] = \
|
|
544
|
+
sec.gauge_length
|
|
545
|
+
try:
|
|
546
|
+
h5_file['default'].attrs['cid'] = \
|
|
547
|
+
sec.headers['default']['attrs']['cid']
|
|
548
|
+
except KeyError:
|
|
549
|
+
pass
|
|
469
550
|
|
|
470
551
|
elif file_format == 'Silixa iDAS':
|
|
471
552
|
if h5_file['Acquisition/Raw[0]/RawData/'].shape[0] != \
|
|
@@ -538,6 +619,24 @@ def _write_h5(sec, fname, raw_fname=None, file_format='auto'):
|
|
|
538
619
|
_update_h5_dataset(h5_file, '/', 'Sampling_interval_in_time',
|
|
539
620
|
[1/sec.fs])
|
|
540
621
|
|
|
622
|
+
elif file_format == 'NEC':
|
|
623
|
+
_update_h5_dataset(h5_file, '/', 'data', sec.data)
|
|
624
|
+
h5_file['data'].attrs['Interval of monitor point'] = sec.dx
|
|
625
|
+
h5_file['data'].attrs['Number of requested location points'] \
|
|
626
|
+
= sec.nch
|
|
627
|
+
h5_file['data'].attrs['Interval time of data'] = \
|
|
628
|
+
1 / sec.fs * 1e3
|
|
629
|
+
if hasattr(sec, scale):
|
|
630
|
+
h5_file['data'].attrs['Radians per digital value'] = \
|
|
631
|
+
sec.scale
|
|
632
|
+
if isinstance(sec.start_time, datatime):
|
|
633
|
+
t0 = sec.start_time.timestamp()
|
|
634
|
+
else:
|
|
635
|
+
t0 = sec.start_time
|
|
636
|
+
h5_file['data'].attrs['Time of sending request'] = t0 * 1e3
|
|
637
|
+
if hasattr(sec, 'gauge_length'):
|
|
638
|
+
h5_file['data'].attrs['Gauge length'] = sec.gauge_length
|
|
639
|
+
|
|
541
640
|
elif file_format == 'FORESEE':
|
|
542
641
|
_update_h5_dataset(h5_file, '/', 'raw', sec.data)
|
|
543
642
|
DataTime = sec.start_time.timestamp() + \
|
|
@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
|
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
setup(
|
|
5
|
-
name='DASPy-toolbox', version='1.2.
|
|
5
|
+
name='DASPy-toolbox', version='1.2.3',
|
|
6
6
|
description=(
|
|
7
7
|
'DASPy is an open-source project dedicated to provide a python package '
|
|
8
8
|
'for DAS (Distributed Acoustic Sensing) data processing, which '
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|