DASPy-toolbox 1.2.0__tar.gz → 1.2.1__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 (32) hide show
  1. {daspy_toolbox-1.2.0 → daspy_toolbox-1.2.1}/DASPy_toolbox.egg-info/PKG-INFO +1 -1
  2. {daspy_toolbox-1.2.0 → daspy_toolbox-1.2.1}/DASPy_toolbox.egg-info/SOURCES.txt +1 -0
  3. {daspy_toolbox-1.2.0 → daspy_toolbox-1.2.1}/PKG-INFO +1 -1
  4. {daspy_toolbox-1.2.0 → daspy_toolbox-1.2.1}/daspy/advanced_tools/denoising.py +2 -2
  5. {daspy_toolbox-1.2.0 → daspy_toolbox-1.2.1}/daspy/basic_tools/preprocessing.py +26 -9
  6. {daspy_toolbox-1.2.0 → daspy_toolbox-1.2.1}/daspy/core/collection.py +28 -28
  7. {daspy_toolbox-1.2.0 → daspy_toolbox-1.2.1}/daspy/core/dasdatetime.py +1 -1
  8. daspy_toolbox-1.2.1/daspy/core/make_example.py +32 -0
  9. {daspy_toolbox-1.2.0 → daspy_toolbox-1.2.1}/daspy/core/read.py +18 -21
  10. {daspy_toolbox-1.2.0 → daspy_toolbox-1.2.1}/daspy/core/section.py +31 -18
  11. {daspy_toolbox-1.2.0 → daspy_toolbox-1.2.1}/daspy/core/write.py +9 -4
  12. {daspy_toolbox-1.2.0 → daspy_toolbox-1.2.1}/setup.py +3 -3
  13. {daspy_toolbox-1.2.0 → daspy_toolbox-1.2.1}/DASPy_toolbox.egg-info/dependency_links.txt +0 -0
  14. {daspy_toolbox-1.2.0 → daspy_toolbox-1.2.1}/DASPy_toolbox.egg-info/entry_points.txt +0 -0
  15. {daspy_toolbox-1.2.0 → daspy_toolbox-1.2.1}/DASPy_toolbox.egg-info/requires.txt +0 -0
  16. {daspy_toolbox-1.2.0 → daspy_toolbox-1.2.1}/DASPy_toolbox.egg-info/top_level.txt +0 -0
  17. {daspy_toolbox-1.2.0 → daspy_toolbox-1.2.1}/LICENSE +0 -0
  18. {daspy_toolbox-1.2.0 → daspy_toolbox-1.2.1}/README.md +0 -0
  19. {daspy_toolbox-1.2.0 → daspy_toolbox-1.2.1}/daspy/__init__.py +0 -0
  20. {daspy_toolbox-1.2.0 → daspy_toolbox-1.2.1}/daspy/advanced_tools/__init__.py +0 -0
  21. {daspy_toolbox-1.2.0 → daspy_toolbox-1.2.1}/daspy/advanced_tools/channel.py +0 -0
  22. {daspy_toolbox-1.2.0 → daspy_toolbox-1.2.1}/daspy/advanced_tools/decomposition.py +0 -0
  23. {daspy_toolbox-1.2.0 → daspy_toolbox-1.2.1}/daspy/advanced_tools/fdct.py +0 -0
  24. {daspy_toolbox-1.2.0 → daspy_toolbox-1.2.1}/daspy/advanced_tools/strain2vel.py +0 -0
  25. {daspy_toolbox-1.2.0 → daspy_toolbox-1.2.1}/daspy/basic_tools/__init__.py +0 -0
  26. {daspy_toolbox-1.2.0 → daspy_toolbox-1.2.1}/daspy/basic_tools/filter.py +0 -0
  27. {daspy_toolbox-1.2.0 → daspy_toolbox-1.2.1}/daspy/basic_tools/freqattributes.py +0 -0
  28. {daspy_toolbox-1.2.0 → daspy_toolbox-1.2.1}/daspy/basic_tools/visualization.py +0 -0
  29. {daspy_toolbox-1.2.0 → daspy_toolbox-1.2.1}/daspy/core/__init__.py +0 -0
  30. {daspy_toolbox-1.2.0 → daspy_toolbox-1.2.1}/daspy/core/example.pkl +0 -0
  31. {daspy_toolbox-1.2.0 → daspy_toolbox-1.2.1}/daspy/core/util.py +0 -0
  32. {daspy_toolbox-1.2.0 → daspy_toolbox-1.2.1}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: DASPy-toolbox
3
- Version: 1.2.0
3
+ Version: 1.2.1
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
@@ -23,6 +23,7 @@ daspy/core/__init__.py
23
23
  daspy/core/collection.py
24
24
  daspy/core/dasdatetime.py
25
25
  daspy/core/example.pkl
26
+ daspy/core/make_example.py
26
27
  daspy/core/read.py
27
28
  daspy/core/section.py
28
29
  daspy/core/util.py
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: DASPy-toolbox
3
- Version: 1.2.0
3
+ Version: 1.2.1
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
  # Purpose: Remove noise from data
2
2
  # Author: Minzhe Hu, Zefeng Li
3
- # Date: 2024.5.13
3
+ # Date: 2025.9.18
4
4
  # Email: hmz2018@mail.ustc.edu.cn
5
5
  import numpy as np
6
6
  from copy import deepcopy
@@ -57,7 +57,7 @@ def common_mode_noise_removal(data, method='median'):
57
57
  common = np.mean(data, 0)
58
58
 
59
59
  xx = np.sum(common ** 2)
60
- data_dn = np.zeros((nch, nt))
60
+ data_dn = np.zeros((nch, nt), dtype=data.dtype)
61
61
  for i in range(nch):
62
62
  xc = np.sum(common * data[i])
63
63
  data_dn[i] = data[i] - xc / xx * common
@@ -1,9 +1,10 @@
1
1
  # Purpose: Some preprocess methods
2
2
  # Author: Minzhe Hu
3
- # Date: 2025.6.25
3
+ # Date: 2025.10.30
4
4
  # Email: hmz2018@mail.ustc.edu.cn
5
5
  import warnings
6
6
  import numpy as np
7
+ from numpy.fft import rfft, irfft, rfftfreq
7
8
  from scipy.signal import detrend
8
9
  from scipy.signal.windows import tukey
9
10
  from daspy.basic_tools.filter import lowpass_cheby_2
@@ -320,7 +321,7 @@ def padding(data, dn, reverse=False):
320
321
  return data_pd
321
322
 
322
323
 
323
- def time_integration(data, fs, c=0):
324
+ def time_integration(data, fs, domain='time', c=0):
324
325
  """
325
326
  Integrate DAS data in time.
326
327
 
@@ -329,10 +330,19 @@ def time_integration(data, fs, c=0):
329
330
  :param c: float. A constant added to the result.
330
331
  :return: Integrated data.
331
332
  """
332
- return np.cumsum(data, axis=1) / fs + c
333
-
334
-
335
- def time_differential(data, fs, prepend=0):
333
+ if domain == 'time':
334
+ return np.cumsum(data, axis=1) / fs + c
335
+ elif domain in ['frequency', 'freq']:
336
+ nsp = data.shape[1]
337
+ freqs = rfftfreq(nsp, d=1/fs)
338
+ spectrum = rfft(data, axis=1)
339
+ H = np.zeros_like(freqs, dtype=complex)
340
+ nonzero = freqs != 0
341
+ H[nonzero] = 1 / (1j * 2 * np.pi * freqs[nonzero])
342
+ return np.real(irfft(spectrum * H))
343
+
344
+
345
+ def time_differential(data, fs, domain='time', prepend=0):
336
346
  """
337
347
  Differentiate DAS data in time.
338
348
 
@@ -342,9 +352,16 @@ def time_differential(data, fs, prepend=0):
342
352
  performing the difference.
343
353
  :return: Differentiated data.
344
354
  """
345
- if prepend == 'mean':
346
- prepend = np.mean(data, axis=1).reshape((-1, 1))
347
- return np.diff(data, axis=1, prepend=prepend) * fs
355
+ if domain == 'time':
356
+ if prepend == 'mean':
357
+ prepend = np.mean(data, axis=1).reshape((-1, 1))
358
+ return np.diff(data, axis=1, prepend=prepend) * fs
359
+ elif domain in ['frequency', 'freq']:
360
+ nsp = data.shape[1]
361
+ freqs = rfftfreq(nsp, d=1./fs)
362
+ spectrum = rfft(data, axis=1)
363
+ H = 1j * 2 * np.pi * freqs
364
+ return np.real(irfft(spectrum * H))
348
365
 
349
366
 
350
367
  def distance_integration(data, dx, c=0):
@@ -1,6 +1,6 @@
1
1
  # Purpose: Module for handling Collection objects.
2
2
  # Author: Minzhe Hu
3
- # Date: 2025.9.15
3
+ # Date: 2025.9.18
4
4
  # Email: hmz2018@mail.ustc.edu.cn
5
5
  import os
6
6
  import warnings
@@ -277,10 +277,11 @@ class Collection(object):
277
277
  if len(flist) == 1:
278
278
  sec = read(flist[0], tmin=tmin, tmax=tmax, **kwargs)
279
279
  else:
280
- sec = read(flist[0], tmin=tmin, **kwargs)
280
+ sec = read(flist[0], tmin=tmin-tolerance, **kwargs)
281
281
  for f in flist[1:-1]:
282
282
  sec += read(f, **kwargs)
283
- sec += read(flist[-1], tmax=tmax, **kwargs)
283
+ sec += read(flist[-1], tmax=tmax+tolerance, **kwargs)
284
+ sec.trimming(tmin=tmin, tmax=tmax)
284
285
  return sec
285
286
  else:
286
287
  self.flist = flist
@@ -326,8 +327,8 @@ class Collection(object):
326
327
  kwargs_list[j]['zi'] = 0
327
328
 
328
329
  def process(self, operations, savepath='./processed', merge=1,
329
- suffix='_pro', ftype=None, dtype=None, save_operations=False,
330
- tolerance=0.5, **read_kwargs):
330
+ suffix='_pro', ftype=None, dtype=None, file_format='auto',
331
+ save_operations=False, tolerance=0.5, **read_kwargs):
331
332
  """
332
333
  :param operations: list or None. Each element of operations list
333
334
  should be [str of method name, dict of kwargs]. None for read
@@ -339,6 +340,8 @@ class Collection(object):
339
340
  :param ftype: None or str. File format for saving. None for automatic
340
341
  detection, or 'pkl', 'pickle', 'tdms', 'h5', 'hdf5', 'segy', 'sgy',
341
342
  'npy'.
343
+ :param file_format: Format in which the file is saved.
344
+ :type file_format: str or function
342
345
  :param dtype: str. The data type of the saved data.
343
346
  :parma save_operations: bool. If True, save the operations to
344
347
  method_list.pkl and kwargs_list.pkl in savepath.
@@ -369,7 +372,8 @@ class Collection(object):
369
372
  warnings.warn(f'{f} is an empty file. Continuous data is '
370
373
  'interrupted here.')
371
374
  if m > 0:
372
- sec_merge.save(filepath, dtype=dtype)
375
+ sec_merge.save(filepath, file_format=file_format,
376
+ dtype=dtype)
373
377
  m = 0
374
378
  self._kwargs_initialization(method_list, kwargs_list)
375
379
  continue
@@ -377,7 +381,8 @@ class Collection(object):
377
381
  sec = read(f, ftype=self.ftype, **read_kwargs)
378
382
  if sec.data.size == 0:
379
383
  if m > 0:
380
- sec_merge.save(filepath, dtype=dtype)
384
+ sec_merge.save(filepath, ftype=ftype,
385
+ file_format=file_format, dtype=dtype)
381
386
  m = 0
382
387
  self._kwargs_initialization(method_list, kwargs_list)
383
388
  continue
@@ -385,23 +390,24 @@ class Collection(object):
385
390
  warnings.warn(f'Error reading {f}: {e}. Continuous data is '
386
391
  'interrupted here.')
387
392
  if m > 0:
388
- sec_merge.save(filepath, dtype=dtype)
393
+ sec_merge.save(filepath, ftype=ftype,
394
+ file_format=file_format, dtype=dtype)
389
395
  m = 0
390
396
  self._kwargs_initialization(method_list, kwargs_list)
391
397
  continue
392
398
  for j, method in enumerate(method_list):
393
399
  if method in ['taper', 'cosine_taper']:
394
400
  if not ((i==0 and kwargs_list[j]['side'] != 'right') or
395
- (i == len(self) - 1 and kwargs_list[j]['side'] !=
396
- 'left')):
401
+ (i == len(self) - 1 and kwargs_list[j]['side']
402
+ != 'left')):
397
403
  continue
398
404
  out = getattr(sec, method)(**kwargs_list[j])
399
405
  if method == 'time_integration':
400
406
  kwargs_list[j]['c'] = sec.data[:, -1].copy()
401
407
  elif method == 'time_differential':
402
408
  kwargs_list[j]['prepend'] = sec.data[:, -1].copy()
403
- elif method in ['bandpass', 'bandstop', 'lowpass', 'highpass',
404
- 'lowpass_cheby_2']:
409
+ elif method in ['bandpass', 'bandstop', 'lowpass',
410
+ 'highpass', 'lowpass_cheby_2']:
405
411
  kwargs_list[j]['zi'] = out
406
412
 
407
413
  if m == 0:
@@ -415,7 +421,8 @@ class Collection(object):
415
421
  warnings.warn(f'The start time of {f} does not correspond '
416
422
  'to the end time of the previous file. '
417
423
  'Continuous data is interrupted here.')
418
- sec_merge.save(filepath, dtype=dtype)
424
+ sec_merge.save(filepath, ftype=ftype,
425
+ file_format=file_format, dtype=dtype)
419
426
  sec_merge = sec
420
427
  f0, f1 = os.path.splitext(os.path.basename(f))
421
428
  f1 = f1 if ftype is None else ftype
@@ -423,10 +430,12 @@ class Collection(object):
423
430
  m = 0
424
431
  m += 1
425
432
  if m == merge:
426
- sec_merge.save(filepath, dtype=dtype)
433
+ sec_merge.save(filepath, ftype=ftype,
434
+ file_format=file_format, dtype=dtype)
427
435
  m = 0
428
436
  if m > 0:
429
- sec_merge.save(filepath, dtype=dtype)
437
+ sec_merge.save(filepath, ftype=ftype, file_format=file_format,
438
+ dtype=dtype)
430
439
  except KeyboardInterrupt as e:
431
440
  with open(method_file, 'wb') as f:
432
441
  pickle.dump(method_list, f)
@@ -452,25 +461,16 @@ class Collection(object):
452
461
  def _create_cascade_method(method_name):
453
462
  def cascade_method(self, savepath='./processed', merge=1,
454
463
  suffix=f'_{method_name}', ftype=None, dtype=None,
455
- save_operations=False, **kwargs):
464
+ file_format='auto', save_operations=False, tolerance=0.5,
465
+ **kwargs):
456
466
  """
457
467
  Automatically generated method for {method_name}.
458
468
  Applies the {method_name} operation to the data and saves the result.
459
-
460
- :param savepath: str. Path to save processed files.
461
- :param merge: int or str. int for merge several processed files into 1.
462
- 'all' for merge all files.
463
- :param suffix: str. Suffix for processed files.
464
- :param ftype: None or str. None for automatic detection, or 'pkl',
465
- 'pickle', 'tdms', 'h5', 'hdf5', 'segy', 'sgy', 'npy'.
466
- :param dtype: str. The data type of the saved data.
467
- :parma save_operations: bool. If True, save the operations to
468
- method_list.pkl and kwargs_list.pkl in savepath.
469
- :param kwargs: dict. Parameters for the {method_name} operation.
470
469
  """
471
470
  operations = [[method_name, kwargs]]
472
471
  self.process(operations, savepath=savepath, merge=merge, suffix=suffix,
473
- ftype=ftype, dtype=dtype, save_operations=save_operations)
472
+ ftype=ftype, dtype=dtype, file_format=file_format,
473
+ save_operations=save_operations, tolerance=tolerance)
474
474
  return cascade_method
475
475
 
476
476
 
@@ -84,7 +84,7 @@ class DASDateTime(datetime):
84
84
  match = re.match(r'(.*)(UTC|GMT)([+-]?\d{1,2})(.*)', date_string,
85
85
  re.IGNORECASE)
86
86
  if match:
87
- dt1, tz_prefix, offset, dt2 = match.groups()
87
+ dt1, _, offset, dt2 = match.groups()
88
88
  offset_hours = int(offset)
89
89
  tz = timezone(timedelta(hours=offset_hours))
90
90
  return cls.strptime(dt1 + dt2, format.replace('%Z', '')).\
@@ -0,0 +1,32 @@
1
+ import h5py
2
+ import numpy as np
3
+ from daspy import read, Section, DASDateTime
4
+ from daspy.core.dasdatetime import utc
5
+
6
+
7
+ origin_time = DASDateTime(2016, 3, 21, 7, 37, 10, 535000, tzinfo=utc)
8
+
9
+ # read DAS data
10
+ dx, fs = 1, 1000
11
+ sec = Section(np.zeros((8721, 0)), dx, fs, data_type='Strain rate')
12
+ filename = ['PoroTomo_iDAS16043_160321073651.h5',
13
+ 'PoroTomo_iDAS16043_160321073721.h5',
14
+ 'PoroTomo_iDAS16043_160321073751.h5',
15
+ 'PoroTomo_iDAS16043_160321073821.h5']
16
+
17
+ first = True
18
+ for fn in filename:
19
+ with h5py.File(fn,'r') as fp:
20
+ if first:
21
+ sec.start_time = DASDateTime.fromtimestamp(fp['t'][0]).astimezone(utc)
22
+ sec.start_channel = fp['channel'][0]
23
+ first = False
24
+ sec += fp['das'][()].T
25
+
26
+ sec.origin_time = origin_time
27
+ sec.trimming(mode=0, xmin=2500, xmax=3000)
28
+ sec.trimming(tmin=origin_time, tmax=origin_time+90)
29
+ sec.downsampling(tint=10)
30
+ sec.trimming(tmin=origin_time+20, tmax=origin_time+70)
31
+
32
+ sec.save('example.py')
@@ -1,6 +1,6 @@
1
1
  # Purpose: Module for reading DAS data.
2
2
  # Author: Minzhe Hu
3
- # Date: 2025.9.15
3
+ # Date: 2025.10.30
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
@@ -188,7 +188,8 @@ def _read_h5(fname, headonly=False, file_format='auto', chmin=None, chmax=None,
188
188
  start_time = DASDateTime.fromisoformat(h5_file[data_key].
189
189
  attrs['start_time'])
190
190
  else:
191
- start_time = DASDateTime.fromtimestamp(float(header[100])).utc()
191
+ start_time = DASDateTime.fromtimestamp(float(header[100])).\
192
+ utc()
192
193
  transpose = True
193
194
  metadata = {'dx': header[1],
194
195
  'fs': header[6] / header[15] / header[98],
@@ -216,13 +217,9 @@ def _read_h5(fname, headonly=False, file_format='auto', chmin=None, chmax=None,
216
217
  fs = float(attrs['FreqRes'])
217
218
  except KeyError:
218
219
  try:
219
- fs = (attrs['PulseRateFreq'][0] /
220
- attrs['SamplingRes'][0]) / 1000
220
+ fs = float(1000 / attrs['Spacing'][1])
221
221
  except KeyError:
222
- try:
223
- fs = 1000 / attrs['Spacing'][1]
224
- except KeyError:
225
- fs = attrs['SamplingRate'][0]
222
+ fs = attrs['PulseRateFreq'][0]
226
223
  time = h5_file[f'{group}/Source1/time']
227
224
  if len(time.shape) == 2: # Febus A1-R
228
225
  start_time = DASDateTime.fromtimestamp(time[0, 0]).utc()
@@ -260,11 +257,9 @@ def _read_h5(fname, headonly=False, file_format='auto', chmin=None, chmax=None,
260
257
  'Smart Earth ZD-DAS', 'Unknown']:
261
258
  dataset = h5_file['Acquisition/Raw[0]/RawData/']
262
259
  attrs = h5_file['Acquisition'].attrs
263
- try:
260
+ if 'NumberOfLoci' in attrs.keys():
264
261
  if dataset.shape[0] != attrs['NumberOfLoci']:
265
262
  transpose = True
266
- except KeyError:
267
- pass
268
263
 
269
264
  try:
270
265
  fs = h5_file['Acquisition/Raw[0]'].attrs['OutputDataRate']
@@ -282,7 +277,7 @@ def _read_h5(fname, headonly=False, file_format='auto', chmin=None, chmax=None,
282
277
  stime = h5_file['Acquisition/Raw[0]/RawDataTime/'][0]
283
278
  except KeyError:
284
279
  stime = 0
285
-
280
+
286
281
  if isinstance(stime, bytes):
287
282
  stime = stime.decode('ascii')
288
283
 
@@ -415,11 +410,14 @@ def _read_h5(fname, headonly=False, file_format='auto', chmin=None, chmax=None,
415
410
  elif transpose:
416
411
  if len(dataset.shape) == 3:
417
412
  fs = int(metadata['fs'])
418
- j0, k0 = divmod(sj.start, fs)
419
- j1, k1 = divmod(sj.stop, fs)
420
- k1 = (j1 - j0) * fs + k1
413
+ fs_b = attrs.get('BlockRate', [1000])[0] / 1e3
414
+ nsp_b = round(fs/fs_b)
415
+ half_ol = round((dataset.shape[1] - nsp_b) / 2)
416
+ j0, k0 = divmod(sj.start, nsp_b)
417
+ j1, k1 = divmod(sj.stop, nsp_b)
418
+ k1 = (j1 - j0) * nsp_b + k1
421
419
  j1 += 1
422
- data = dataset[j0:j1, :, si]
420
+ data = dataset[j0:j1, half_ol:half_ol+nsp_b, si]
423
421
  data = data.reshape((-1, data.shape[-1]))[k0:k1, :].T
424
422
  else:
425
423
  data = dataset[sj, si].T
@@ -444,9 +442,9 @@ def _read_tdms(fname, headonly=False, file_format='auto', chmin=None,
444
442
  version = float(properties['iDASVersion'][:3])
445
443
  if version < 2.3:
446
444
  file_format = 'Silixa iDAS'
447
- elif 2.3 <= version < 2.6:
445
+ elif 2.3 <= version < 2.7:
448
446
  file_format = 'Silixa iDAS-v2'
449
- elif version >= 2.6:
447
+ elif version >= 2.7:
450
448
  file_format = 'Silixa iDAS-v3'
451
449
  elif group_name == ['DAS']:
452
450
  key = 'DAS'
@@ -483,12 +481,11 @@ def _read_tdms(fname, headonly=False, file_format='auto', chmin=None,
483
481
  if isinstance(properties[time_key], str):
484
482
  start_time = DASDateTime.fromisoformat(
485
483
  properties[time_key])
484
+ break
486
485
  elif isinstance(properties[time_key], np.datetime64):
487
486
  start_time = DASDateTime.from_datetime(
488
487
  properties[time_key].item())
489
- else:
490
- continue
491
- break
488
+ break
492
489
  metadata['start_time'] = start_time
493
490
  if 'GaugeLength' in properties.keys():
494
491
  metadata['gauge_length'] = properties['GaugeLength']
@@ -1,6 +1,6 @@
1
1
  # Purpose: Module for handling Section objects.
2
2
  # Author: Minzhe Hu
3
- # Date: 2025.9.15
3
+ # Date: 2025.10.31
4
4
  # Email: hmz2018@mail.ustc.edu.cn
5
5
  import warnings
6
6
  import os
@@ -150,15 +150,15 @@ class Section(object):
150
150
  else:
151
151
  raise TypeError('The input should be Section or np.ndarray.')
152
152
 
153
- if len(data) != self.nch:
154
- if len(data[0]) == self.nch:
155
- data = data.T
156
- else:
157
- raise ValueError('These two Sections have different number of '
158
- 'channels, please check.')
159
- if out.data is None:
153
+ if (out.data is None) or (out.nch * out.nsp == 0):
160
154
  out.data = data
161
155
  else:
156
+ if len(data) != self.nch:
157
+ if len(data[0]) == self.nch:
158
+ data = data.T
159
+ else:
160
+ raise ValueError('These two Sections have different number '
161
+ 'of channels, please check.')
162
162
  out.data = np.hstack((out.data, data))
163
163
 
164
164
  return out
@@ -548,7 +548,7 @@ class Section(object):
548
548
  self.start_distance += channel[0] * self.dx
549
549
  return self
550
550
  else:
551
- return data
551
+ return data * self.scale
552
552
 
553
553
  def plot(self, xmode='distance', tmode='origin', obj='waveform',
554
554
  kwargs_pro={}, **kwargs):
@@ -645,7 +645,10 @@ class Section(object):
645
645
  if tmode == 'origin':
646
646
  if hasattr(self, 'origin_time'):
647
647
  kwargs['t0'] -= self.origin_time
648
- kwargs.setdefault('ylabel', 'Times(s) after occurance')
648
+ if ('transpose' in kwargs.keys()) and kwargs['transpose']:
649
+ kwargs.setdefault('xlabel', 'Times(s) after occurance')
650
+ else:
651
+ kwargs.setdefault('ylabel', 'Times(s) after occurance')
649
652
  else:
650
653
  tmode == 'start'
651
654
  if tmode == 'start':
@@ -765,18 +768,26 @@ class Section(object):
765
768
  self.data = cosine_taper(self.data, p=p, side=side)
766
769
  return self
767
770
 
768
- def downsampling(self, xint=None, tint=None, stack=True,
771
+ def downsampling(self, xint=None, tint=None, fs=None, dx=None, stack=True,
769
772
  lowpass_filter=True):
770
773
  """
771
774
  Downsample DAS data.
772
775
 
773
776
  :param xint: int. Spatial downsampling factor.
774
777
  :param tint: int. Time downsampling factor.
778
+ :param fs: float. Target sampling rate after downsampling. It is used
779
+ if tint is None.
780
+ :param dx: float. Target channel interval after downsampling. It is
781
+ used if xint is None.
775
782
  :param stack: bool. If True, stacking will replace decimation.
776
783
  :param lowpass_filter: bool. Lowpass cheby2 filter before time
777
784
  downsampling or not.
778
785
  :return: Downsampled data.
779
786
  """
787
+ if xint is None and dx is not None:
788
+ xint = round(dx / self.dx)
789
+ if tint is None and fs is not None:
790
+ tint = round(self.fs / fs)
780
791
  self.data = downsampling(self.data, xint=xint, tint=tint, stack=stack,
781
792
  lowpass_filter=lowpass_filter)
782
793
  if xint and xint > 1:
@@ -851,25 +862,26 @@ class Section(object):
851
862
  return self
852
863
  warnings.warn('Unable to convert data type.')
853
864
 
854
- def time_integration(self, c=0):
865
+ def time_integration(self, domain='time', c=0):
855
866
  """
856
867
  Integrate DAS data in time.
857
868
 
858
869
  :param c: float. A constant added to the result.
859
870
  """
860
- self.data = time_integration(self.data, self.fs, c=c)
871
+ self.data = time_integration(self.data, self.fs, domain=domain, c=c)
861
872
  if hasattr(self, 'data_type'):
862
873
  self._time_int_dif_attr(mode=1)
863
874
  return self
864
875
 
865
- def time_differential(self, prepend=0):
876
+ def time_differential(self, domain='time', prepend=0):
866
877
  """
867
878
  Differentiate DAS data in time.
868
879
 
869
- :param prepend: 'mean' or values to prepend to `data` along axis prior to
870
- performing the difference.
880
+ :param prepend: 'mean' or values to prepend to `data` along axis prior
881
+ to performing the difference.
871
882
  """
872
- self.data = time_differential(self.data, self.fs, prepend=prepend)
883
+ self.data = time_differential(self.data, self.fs, domain=domain,
884
+ prepend=prepend)
873
885
  if hasattr(self, 'data_type'):
874
886
  self._time_int_dif_attr(mode=-1)
875
887
  return self
@@ -901,7 +913,8 @@ class Section(object):
901
913
  None.
902
914
  """
903
915
  if zi is None:
904
- self.data = bandpass(self.data, self.fs, freqmin, freqmax, **kwargs)
916
+ self.data = bandpass(self.data, self.fs, freqmin, freqmax,
917
+ **kwargs)
905
918
  return self
906
919
  else:
907
920
  self.data, zf = bandpass(self.data, self.fs, freqmin, freqmax,
@@ -1,6 +1,6 @@
1
1
  # Purpose: Module for writing DAS data.
2
2
  # Author: Minzhe Hu
3
- # Date: 2025.9.15
3
+ # Date: 2025.9.18
4
4
  # Email: hmz2018@mail.ustc.edu.cn
5
5
  import os
6
6
  import warnings
@@ -168,7 +168,6 @@ def _write_h5(sec, fname, raw_fname=None, file_format='auto'):
168
168
  start_time = sec.start_time.utc() if \
169
169
  isinstance(sec.start_time, datetime) else \
170
170
  DASDateTime.fromtimestamp(sec.start_time)
171
-
172
171
  if file_format in ['OptaSense ODH4+', 'OptaSense QuantX',
173
172
  'Unknown']:
174
173
  h5_file.get('Acquisition/Raw[0]/').create_dataset('RawData',
@@ -186,6 +185,7 @@ def _write_h5(sec, fname, raw_fname=None, file_format='auto'):
186
185
  attrs['PartStartTime'] = stime_str
187
186
  h5_file['Acquisition'].attrs['MeasurementStartTime'] = \
188
187
  stime_str
188
+ h5_file['Acquisition'].attrs['NumberOfLoci'] = sec.nch
189
189
  stimestamp = start_time.timestamp()
190
190
  datatime = (np.arange(stimestamp, stimestamp + sec.nsp /
191
191
  sec.fs, 1 / sec.fs) * 1e6).astype(int)
@@ -434,9 +434,14 @@ def _write_h5(sec, fname, raw_fname=None, file_format='auto'):
434
434
  elif file_format in ['OptaSense ODH4+', 'OptaSense QuantX',
435
435
  'Silixa iDAS-MG', 'Sintela Onyx v1.0',
436
436
  'Smart Earth ZD-DAS', 'Unknown']:
437
+ if file_format in ['Silixa iDAS-MG', 'Sintela Onyx v1.0',
438
+ 'Smart Earth ZD-DAS']:
439
+ _update_h5_dataset(h5_file, 'Acquisition/Raw[0]/',
440
+ 'RawData', sec.data.T)
441
+ else:
442
+ _update_h5_dataset(h5_file, 'Acquisition/Raw[0]/',
443
+ 'RawData', sec.data)
437
444
  h5_file['Acquisition'].attrs['NumberOfLoci'] = sec.nch
438
- _update_h5_dataset(h5_file, 'Acquisition/Raw[0]/', 'RawData',
439
- sec.data)
440
445
  if isinstance(sec.start_time, datetime):
441
446
  if isinstance(h5_file['Acquisition/Raw[0]/RawData'].
442
447
  attrs['PartStartTime'], bytes):
@@ -2,14 +2,14 @@ from setuptools import setup, find_packages
2
2
 
3
3
 
4
4
  setup(
5
- name='DASPy-toolbox', version='1.2.0',
5
+ name='DASPy-toolbox', version='1.2.1',
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 '
9
9
  'comprises classic seismic data processing techniques and Specialized '
10
10
  'algorithms for DAS applications.'
11
11
  ),
12
- long_description=open('README.md').read(),
12
+ long_description=open('README.md', encoding="utf-8").read(),
13
13
  author='Minzhe Hu, Zefeng Li',
14
14
  author_email='hmz2018@mail.ustc.edu.cn',
15
15
  maintainer='Minzhe Hu',
@@ -17,7 +17,7 @@ setup(
17
17
  license='MIT License',
18
18
  url='https://github.com/HMZ-03/DASPy',
19
19
  packages=find_packages(),
20
- entry_points={
20
+ entry_points={
21
21
  'console_scripts': [
22
22
  'daspy = daspy.main:main',
23
23
  ]
File without changes
File without changes
File without changes