pyckster 26.2.3__py3-none-any.whl → 26.2.4__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.
- pyckster/__init__.py +1 -1
- pyckster/core.py +400 -32
- pyckster/obspy_utils.py +59 -32
- {pyckster-26.2.3.dist-info → pyckster-26.2.4.dist-info}/METADATA +1 -1
- {pyckster-26.2.3.dist-info → pyckster-26.2.4.dist-info}/RECORD +9 -9
- {pyckster-26.2.3.dist-info → pyckster-26.2.4.dist-info}/WHEEL +0 -0
- {pyckster-26.2.3.dist-info → pyckster-26.2.4.dist-info}/entry_points.txt +0 -0
- {pyckster-26.2.3.dist-info → pyckster-26.2.4.dist-info}/licenses/LICENCE +0 -0
- {pyckster-26.2.3.dist-info → pyckster-26.2.4.dist-info}/top_level.txt +0 -0
pyckster/__init__.py
CHANGED
|
@@ -15,7 +15,7 @@ except ImportError:
|
|
|
15
15
|
pass # matplotlib not available, that's fine
|
|
16
16
|
|
|
17
17
|
# Define version and metadata in one place
|
|
18
|
-
__version__ = "26.2.
|
|
18
|
+
__version__ = "26.2.4"
|
|
19
19
|
__author__ = "Sylvain Pasquet"
|
|
20
20
|
__email__ = "sylvain.pasquet@sorbonne-universite.fr"
|
|
21
21
|
__license__ = "GPLv3"
|
pyckster/core.py
CHANGED
|
@@ -1714,6 +1714,7 @@ class MainWindow(QMainWindow):
|
|
|
1714
1714
|
|
|
1715
1715
|
# Create a QListWidget for file names and add it to the left
|
|
1716
1716
|
self.fileListWidget = QListWidget()
|
|
1717
|
+
self.fileListWidget.setSelectionMode(QListWidget.ExtendedSelection) # Enable multi-selection
|
|
1717
1718
|
self.fileListWidget.itemSelectionChanged.connect(self.onFileSelectionChanged)
|
|
1718
1719
|
self.fileListWidget.setMinimumWidth(50) # Set minimum width
|
|
1719
1720
|
leftLayout.addWidget(self.fileListWidget)
|
|
@@ -2262,28 +2263,36 @@ class MainWindow(QMainWindow):
|
|
|
2262
2263
|
self.openFileAction.triggered.connect(self.openFile)
|
|
2263
2264
|
|
|
2264
2265
|
# Create QAction for importing ASCII matrix
|
|
2265
|
-
self.importAsciiAction = QAction('Import ASCII matrix
|
|
2266
|
+
self.importAsciiAction = QAction('Import ASCII matrix', self)
|
|
2266
2267
|
self.fileMenu.addAction(self.importAsciiAction)
|
|
2267
2268
|
self.importAsciiAction.triggered.connect(self.importAsciiMatrix)
|
|
2268
2269
|
|
|
2269
2270
|
# Create QAction for importing ASCII archive
|
|
2270
|
-
self.importAsciiArchiveAction = QAction('Import ASCII archive
|
|
2271
|
+
self.importAsciiArchiveAction = QAction('Import ASCII archive', self)
|
|
2271
2272
|
self.fileMenu.addAction(self.importAsciiArchiveAction)
|
|
2272
2273
|
self.importAsciiArchiveAction.triggered.connect(self.importAsciiArchive)
|
|
2273
2274
|
|
|
2274
2275
|
# Add separator
|
|
2275
2276
|
self.fileMenu.addSeparator()
|
|
2276
2277
|
|
|
2277
|
-
# Create
|
|
2278
|
-
self.
|
|
2279
|
-
|
|
2278
|
+
# Create a submenu for stacking shots
|
|
2279
|
+
self.stackSubMenu = self.fileMenu.addMenu('Stack...')
|
|
2280
|
+
|
|
2281
|
+
# Create QAction for stacking shots with same source position
|
|
2282
|
+
self.stackShotsAction = QAction('shots at same position', self)
|
|
2283
|
+
self.stackSubMenu.addAction(self.stackShotsAction)
|
|
2280
2284
|
self.stackShotsAction.triggered.connect(self.stackShots)
|
|
2281
2285
|
|
|
2286
|
+
# Create QAction for stacking selected shots
|
|
2287
|
+
self.stackSelectedShotsAction = QAction('selected shots', self)
|
|
2288
|
+
self.stackSubMenu.addAction(self.stackSelectedShotsAction)
|
|
2289
|
+
self.stackSelectedShotsAction.triggered.connect(self.stackSelectedShots)
|
|
2290
|
+
|
|
2282
2291
|
# Add separator
|
|
2283
2292
|
self.fileMenu.addSeparator()
|
|
2284
2293
|
|
|
2285
2294
|
# Create a submenu for saving single files
|
|
2286
|
-
self.saveSingleFileSubMenu = self.fileMenu.addMenu('Save current shot')
|
|
2295
|
+
self.saveSingleFileSubMenu = self.fileMenu.addMenu('Save current shot...')
|
|
2287
2296
|
|
|
2288
2297
|
# Create QAction for saving current file in SEGY
|
|
2289
2298
|
self.saveSingleFileSegyAction = QAction('in a SEGY file', self)
|
|
@@ -2301,7 +2310,7 @@ class MainWindow(QMainWindow):
|
|
|
2301
2310
|
self.saveSingleFileAsciiAction.triggered.connect(self.saveSingleFileASCII)
|
|
2302
2311
|
|
|
2303
2312
|
# Create a submenu for saving all files
|
|
2304
|
-
self.saveFileSubMenu = self.fileMenu.addMenu('Save all shots')
|
|
2313
|
+
self.saveFileSubMenu = self.fileMenu.addMenu('Save all shots...')
|
|
2305
2314
|
|
|
2306
2315
|
# Create QAction for saving all files in SEGY
|
|
2307
2316
|
self.saveAllFilesSegyAction = QAction('in separate SEGY files', self)
|
|
@@ -2331,10 +2340,10 @@ class MainWindow(QMainWindow):
|
|
|
2331
2340
|
# Add separator
|
|
2332
2341
|
self.fileMenu.addSeparator()
|
|
2333
2342
|
|
|
2334
|
-
# Create QAction for removing
|
|
2335
|
-
self.removeShotAction = QAction('Remove
|
|
2343
|
+
# Create QAction for removing selected files
|
|
2344
|
+
self.removeShotAction = QAction('Remove selected shot(s)', self)
|
|
2336
2345
|
self.fileMenu.addAction(self.removeShotAction)
|
|
2337
|
-
self.removeShotAction.triggered.connect(self.
|
|
2346
|
+
self.removeShotAction.triggered.connect(self.removeSelectedShots)
|
|
2338
2347
|
|
|
2339
2348
|
# Create QAction for clearing the memory
|
|
2340
2349
|
self.clearMemoryAction = QAction('Clear memory', self)
|
|
@@ -12876,7 +12885,17 @@ class MainWindow(QMainWindow):
|
|
|
12876
12885
|
if show_cursor:
|
|
12877
12886
|
QApplication.restoreOverrideCursor()
|
|
12878
12887
|
|
|
12888
|
+
def getSelectedShotIndices(self):
|
|
12889
|
+
"""Helper function to get indices of selected shots in the file list.
|
|
12890
|
+
Returns a sorted list of indices (0-based)."""
|
|
12891
|
+
selected_items = self.fileListWidget.selectedItems()
|
|
12892
|
+
if not selected_items:
|
|
12893
|
+
return []
|
|
12894
|
+
selected_indices = [self.fileListWidget.row(item) for item in selected_items]
|
|
12895
|
+
return sorted(selected_indices)
|
|
12896
|
+
|
|
12879
12897
|
def removeShot(self):
|
|
12898
|
+
"""Legacy function - kept for backward compatibility. Use removeSelectedShots instead."""
|
|
12880
12899
|
if self.currentIndex is not None:
|
|
12881
12900
|
# Remove the current file from the lists
|
|
12882
12901
|
for attr in self.attributes_to_initialize:
|
|
@@ -12900,6 +12919,59 @@ class MainWindow(QMainWindow):
|
|
|
12900
12919
|
else:
|
|
12901
12920
|
QMessageBox.information(self, "No Shots Remaining", "No shots remaining. Memory will be cleared.")
|
|
12902
12921
|
self.initMemory()
|
|
12922
|
+
|
|
12923
|
+
def removeSelectedShots(self):
|
|
12924
|
+
"""Remove all selected shots from the file list."""
|
|
12925
|
+
# Get selected row indices
|
|
12926
|
+
selected_items = self.fileListWidget.selectedItems()
|
|
12927
|
+
if not selected_items:
|
|
12928
|
+
QMessageBox.warning(self, "No Selection", "No shots selected. Please select one or more shots to remove.")
|
|
12929
|
+
return
|
|
12930
|
+
|
|
12931
|
+
# Get the indices of selected items
|
|
12932
|
+
selected_indices = [self.fileListWidget.row(item) for item in selected_items]
|
|
12933
|
+
selected_indices.sort(reverse=True) # Sort in reverse to remove from end first
|
|
12934
|
+
|
|
12935
|
+
# Confirm removal if multiple shots selected
|
|
12936
|
+
if len(selected_indices) > 1:
|
|
12937
|
+
reply = QMessageBox.question(self, "Confirm Removal",
|
|
12938
|
+
f"Are you sure you want to remove {len(selected_indices)} selected shot(s)?",
|
|
12939
|
+
QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
|
|
12940
|
+
if reply == QMessageBox.No:
|
|
12941
|
+
return
|
|
12942
|
+
|
|
12943
|
+
# Remove selected shots (from end to beginning to maintain indices)
|
|
12944
|
+
for idx in selected_indices:
|
|
12945
|
+
for attr in self.attributes_to_initialize:
|
|
12946
|
+
getattr(self, attr).pop(idx)
|
|
12947
|
+
|
|
12948
|
+
# Update current index to the first remaining shot or 0
|
|
12949
|
+
if self.streams:
|
|
12950
|
+
# Set to the first valid index that's not removed
|
|
12951
|
+
remaining_count = len(self.fileNames)
|
|
12952
|
+
if remaining_count > 0:
|
|
12953
|
+
# Try to select the shot after the last removed one, or the last available
|
|
12954
|
+
new_index = min(selected_indices) if selected_indices else 0
|
|
12955
|
+
self.currentIndex = min(new_index, remaining_count - 1)
|
|
12956
|
+
else:
|
|
12957
|
+
self.currentIndex = 0
|
|
12958
|
+
else:
|
|
12959
|
+
self.currentIndex = 0
|
|
12960
|
+
|
|
12961
|
+
# Update the plot type dictionary
|
|
12962
|
+
self.updatePlotTypeDict()
|
|
12963
|
+
|
|
12964
|
+
# Update the file list display
|
|
12965
|
+
self.updateFileListDisplay()
|
|
12966
|
+
|
|
12967
|
+
# Update selected file in the list display
|
|
12968
|
+
if self.streams:
|
|
12969
|
+
self.fileListWidget.setCurrentRow(self.currentIndex)
|
|
12970
|
+
# Update the plot
|
|
12971
|
+
self.updatePlots()
|
|
12972
|
+
else:
|
|
12973
|
+
QMessageBox.information(self, "No Shots Remaining", "No shots remaining. Memory will be cleared.")
|
|
12974
|
+
self.initMemory()
|
|
12903
12975
|
|
|
12904
12976
|
#######################################
|
|
12905
12977
|
# Saving shot functions
|
|
@@ -16806,9 +16878,18 @@ class MainWindow(QMainWindow):
|
|
|
16806
16878
|
|
|
16807
16879
|
def batchEditFFID(self):
|
|
16808
16880
|
if self.streams:
|
|
16881
|
+
# Get selected shot indices for default values (only if 2+ shots selected)
|
|
16882
|
+
selected_indices = self.getSelectedShotIndices()
|
|
16883
|
+
if len(selected_indices) >= 2:
|
|
16884
|
+
first_source_default = selected_indices[0] + 1
|
|
16885
|
+
last_source_default = selected_indices[-1] + 1
|
|
16886
|
+
else:
|
|
16887
|
+
first_source_default = 1
|
|
16888
|
+
last_source_default = len(self.streams)
|
|
16889
|
+
|
|
16809
16890
|
parameters = [
|
|
16810
|
-
{'label': 'First Source #', 'initial_value':
|
|
16811
|
-
{'label': 'Last Source #', 'initial_value':
|
|
16891
|
+
{'label': 'First Source #', 'initial_value': first_source_default, 'type': 'int'},
|
|
16892
|
+
{'label': 'Last Source #', 'initial_value': last_source_default, 'type': 'int'},
|
|
16812
16893
|
{'label': 'First FFID', 'initial_value': self.ffid[0], 'type': 'int'},
|
|
16813
16894
|
{'label': 'Increment', 'initial_value': 1, 'type': 'int'}
|
|
16814
16895
|
]
|
|
@@ -16845,9 +16926,18 @@ class MainWindow(QMainWindow):
|
|
|
16845
16926
|
|
|
16846
16927
|
def batchEditDelay(self):
|
|
16847
16928
|
if self.streams:
|
|
16929
|
+
# Get selected shot indices for default values (only if 2+ shots selected)
|
|
16930
|
+
selected_indices = self.getSelectedShotIndices()
|
|
16931
|
+
if len(selected_indices) >= 2:
|
|
16932
|
+
first_source_default = selected_indices[0] + 1
|
|
16933
|
+
last_source_default = selected_indices[-1] + 1
|
|
16934
|
+
else:
|
|
16935
|
+
first_source_default = 1
|
|
16936
|
+
last_source_default = len(self.streams)
|
|
16937
|
+
|
|
16848
16938
|
parameters = [
|
|
16849
|
-
{'label': 'First Source #', 'initial_value':
|
|
16850
|
-
{'label': 'Last Source #', 'initial_value':
|
|
16939
|
+
{'label': 'First Source #', 'initial_value': first_source_default, 'type': 'int'},
|
|
16940
|
+
{'label': 'Last Source #', 'initial_value': last_source_default, 'type': 'int'},
|
|
16851
16941
|
{'label': 'Delay (in s)', 'initial_value': self.delay[self.currentIndex], 'type': 'float'},
|
|
16852
16942
|
]
|
|
16853
16943
|
|
|
@@ -16925,9 +17015,18 @@ class MainWindow(QMainWindow):
|
|
|
16925
17015
|
|
|
16926
17016
|
def batchEditSourcePosition(self):
|
|
16927
17017
|
if self.streams:
|
|
17018
|
+
# Get selected shot indices for default values (only if 2+ shots selected)
|
|
17019
|
+
selected_indices = self.getSelectedShotIndices()
|
|
17020
|
+
if len(selected_indices) >= 2:
|
|
17021
|
+
first_source_default = selected_indices[0] + 1
|
|
17022
|
+
last_source_default = selected_indices[-1] + 1
|
|
17023
|
+
else:
|
|
17024
|
+
first_source_default = 1
|
|
17025
|
+
last_source_default = len(self.streams)
|
|
17026
|
+
|
|
16928
17027
|
parameters = [
|
|
16929
|
-
{'label': 'First Source #', 'initial_value':
|
|
16930
|
-
{'label': 'Last Source #', 'initial_value':
|
|
17028
|
+
{'label': 'First Source #', 'initial_value': first_source_default, 'type': 'int'},
|
|
17029
|
+
{'label': 'Last Source #', 'initial_value': last_source_default, 'type': 'int'},
|
|
16931
17030
|
{'label': 'Skip every N sources', 'initial_value': 0, 'type': 'int'},
|
|
16932
17031
|
{'label': 'First Source Position (in m)', 'initial_value': self.source_position[0], 'type': 'float'},
|
|
16933
17032
|
{'label': 'Last Source Position (in m)', 'initial_value': '', 'type': 'float_or_empty'},
|
|
@@ -17033,9 +17132,18 @@ class MainWindow(QMainWindow):
|
|
|
17033
17132
|
|
|
17034
17133
|
def batchEditTracePosition(self):
|
|
17035
17134
|
if self.streams:
|
|
17135
|
+
# Get selected shot indices for default values (only if 2+ shots selected)
|
|
17136
|
+
selected_indices = self.getSelectedShotIndices()
|
|
17137
|
+
if len(selected_indices) >= 2:
|
|
17138
|
+
first_source_default = selected_indices[0] + 1
|
|
17139
|
+
last_source_default = selected_indices[-1] + 1
|
|
17140
|
+
else:
|
|
17141
|
+
first_source_default = 1
|
|
17142
|
+
last_source_default = len(self.streams)
|
|
17143
|
+
|
|
17036
17144
|
parameters = [
|
|
17037
|
-
{'label': 'First Source #', 'initial_value':
|
|
17038
|
-
{'label': 'Last Source #', 'initial_value':
|
|
17145
|
+
{'label': 'First Source #', 'initial_value': first_source_default, 'type': 'int'},
|
|
17146
|
+
{'label': 'Last Source #', 'initial_value': last_source_default, 'type': 'int'},
|
|
17039
17147
|
{'label': 'First Trace #', 'initial_value': 1, 'type': 'int'},
|
|
17040
17148
|
{'label': 'Last Trace #', 'initial_value': len(self.trace_position[self.currentIndex]), 'type': 'int'},
|
|
17041
17149
|
{'label': 'First Trace Position (in m)', 'initial_value': self.trace_position[self.currentIndex][0], 'type': 'float'},
|
|
@@ -17095,9 +17203,18 @@ class MainWindow(QMainWindow):
|
|
|
17095
17203
|
|
|
17096
17204
|
def batchSwapTraces(self):
|
|
17097
17205
|
if self.streams:
|
|
17206
|
+
# Get selected shot indices for default values (only if 2+ shots selected)
|
|
17207
|
+
selected_indices = self.getSelectedShotIndices()
|
|
17208
|
+
if len(selected_indices) >= 2:
|
|
17209
|
+
first_source_default = selected_indices[0] + 1
|
|
17210
|
+
last_source_default = selected_indices[-1] + 1
|
|
17211
|
+
else:
|
|
17212
|
+
first_source_default = 1
|
|
17213
|
+
last_source_default = len(self.streams)
|
|
17214
|
+
|
|
17098
17215
|
parameters = [
|
|
17099
|
-
{'label': 'First Source #', 'initial_value':
|
|
17100
|
-
{'label': 'Last Source #', 'initial_value':
|
|
17216
|
+
{'label': 'First Source #', 'initial_value': first_source_default, 'type': 'int'},
|
|
17217
|
+
{'label': 'Last Source #', 'initial_value': last_source_default, 'type': 'int'},
|
|
17101
17218
|
{'label': 'First Trace # to swap', 'initial_value': 1, 'type': 'int'},
|
|
17102
17219
|
{'label': 'Second Trace # to swap', 'initial_value': 2, 'type': 'int'}
|
|
17103
17220
|
]
|
|
@@ -17163,9 +17280,18 @@ class MainWindow(QMainWindow):
|
|
|
17163
17280
|
|
|
17164
17281
|
def batchRemoveTraces(self):
|
|
17165
17282
|
if self.streams:
|
|
17283
|
+
# Get selected shot indices for default values (only if 2+ shots selected)
|
|
17284
|
+
selected_indices = self.getSelectedShotIndices()
|
|
17285
|
+
if len(selected_indices) >= 2:
|
|
17286
|
+
first_source_default = selected_indices[0] + 1
|
|
17287
|
+
last_source_default = selected_indices[-1] + 1
|
|
17288
|
+
else:
|
|
17289
|
+
first_source_default = 1
|
|
17290
|
+
last_source_default = len(self.streams)
|
|
17291
|
+
|
|
17166
17292
|
parameters = [
|
|
17167
|
-
{'label': 'First Source #', 'initial_value':
|
|
17168
|
-
{'label': 'Last Source #', 'initial_value':
|
|
17293
|
+
{'label': 'First Source #', 'initial_value': first_source_default, 'type': 'int'},
|
|
17294
|
+
{'label': 'Last Source #', 'initial_value': last_source_default, 'type': 'int'},
|
|
17169
17295
|
{'label': 'First Trace # to remove', 'initial_value': 1, 'type': 'int'},
|
|
17170
17296
|
{'label': 'Last Trace # to remove', 'initial_value': len(self.trace_position[self.currentIndex]), 'type': 'int'}
|
|
17171
17297
|
]
|
|
@@ -17229,9 +17355,18 @@ class MainWindow(QMainWindow):
|
|
|
17229
17355
|
|
|
17230
17356
|
def batchMoveTraces(self):
|
|
17231
17357
|
if self.streams:
|
|
17358
|
+
# Get selected shot indices for default values (only if 2+ shots selected)
|
|
17359
|
+
selected_indices = self.getSelectedShotIndices()
|
|
17360
|
+
if len(selected_indices) >= 2:
|
|
17361
|
+
first_source_default = selected_indices[0] + 1
|
|
17362
|
+
last_source_default = selected_indices[-1] + 1
|
|
17363
|
+
else:
|
|
17364
|
+
first_source_default = 1
|
|
17365
|
+
last_source_default = len(self.streams)
|
|
17366
|
+
|
|
17232
17367
|
parameters = [
|
|
17233
|
-
{'label': 'First Source #', 'initial_value':
|
|
17234
|
-
{'label': 'Last Source #', 'initial_value':
|
|
17368
|
+
{'label': 'First Source #', 'initial_value': first_source_default, 'type': 'int'},
|
|
17369
|
+
{'label': 'Last Source #', 'initial_value': last_source_default, 'type': 'int'},
|
|
17235
17370
|
{'label': 'First Trace # to move', 'initial_value': 1, 'type': 'int'},
|
|
17236
17371
|
{'label': 'Last Trace # to move', 'initial_value': 1, 'type': 'int'},
|
|
17237
17372
|
{'label': 'New Position', 'initial_value': 1, 'type': 'int'}
|
|
@@ -17298,9 +17433,18 @@ class MainWindow(QMainWindow):
|
|
|
17298
17433
|
|
|
17299
17434
|
def batchMuteTraces(self):
|
|
17300
17435
|
if self.streams:
|
|
17436
|
+
# Get selected shot indices for default values (only if 2+ shots selected)
|
|
17437
|
+
selected_indices = self.getSelectedShotIndices()
|
|
17438
|
+
if len(selected_indices) >= 2:
|
|
17439
|
+
first_source_default = selected_indices[0] + 1
|
|
17440
|
+
last_source_default = selected_indices[-1] + 1
|
|
17441
|
+
else:
|
|
17442
|
+
first_source_default = 1
|
|
17443
|
+
last_source_default = len(self.streams)
|
|
17444
|
+
|
|
17301
17445
|
parameters = [
|
|
17302
|
-
{'label': 'First Source #', 'initial_value':
|
|
17303
|
-
{'label': 'Last Source #', 'initial_value':
|
|
17446
|
+
{'label': 'First Source #', 'initial_value': first_source_default, 'type': 'int'},
|
|
17447
|
+
{'label': 'Last Source #', 'initial_value': last_source_default, 'type': 'int'},
|
|
17304
17448
|
{'label': 'First Trace # to mute', 'initial_value': 1, 'type': 'int'},
|
|
17305
17449
|
{'label': 'Last Trace # to mute', 'initial_value': len(self.trace_position[self.currentIndex]), 'type': 'int'}
|
|
17306
17450
|
]
|
|
@@ -17350,11 +17494,20 @@ class MainWindow(QMainWindow):
|
|
|
17350
17494
|
def batchInsertMutedTraces(self):
|
|
17351
17495
|
"""Batch insert zero traces for a range of shots."""
|
|
17352
17496
|
if self.streams:
|
|
17497
|
+
# Get selected shot indices for default values (only if 2+ shots selected)
|
|
17498
|
+
selected_indices = self.getSelectedShotIndices()
|
|
17499
|
+
if len(selected_indices) >= 2:
|
|
17500
|
+
first_source_default = selected_indices[0] + 1
|
|
17501
|
+
last_source_default = selected_indices[-1] + 1
|
|
17502
|
+
else:
|
|
17503
|
+
first_source_default = 1
|
|
17504
|
+
last_source_default = len(self.streams)
|
|
17505
|
+
|
|
17353
17506
|
n_traces = len(self.trace_position[self.currentIndex]) if self.currentIndex < len(self.trace_position) else 1
|
|
17354
17507
|
|
|
17355
17508
|
parameters = [
|
|
17356
|
-
{'label': 'First Source #', 'initial_value':
|
|
17357
|
-
{'label': 'Last Source #', 'initial_value':
|
|
17509
|
+
{'label': 'First Source #', 'initial_value': first_source_default, 'type': 'int'},
|
|
17510
|
+
{'label': 'Last Source #', 'initial_value': last_source_default, 'type': 'int'},
|
|
17358
17511
|
{'label': 'Insert after Trace # (0 for beginning)', 'initial_value': n_traces, 'type': 'int'},
|
|
17359
17512
|
{'label': 'Number of traces to insert', 'initial_value': 1, 'type': 'int'}
|
|
17360
17513
|
]
|
|
@@ -17407,9 +17560,18 @@ class MainWindow(QMainWindow):
|
|
|
17407
17560
|
def batchZeroPadTraces(self):
|
|
17408
17561
|
"""Batch zero pad all traces for a range of shots."""
|
|
17409
17562
|
if self.streams:
|
|
17563
|
+
# Get selected shot indices for default values (only if 2+ shots selected)
|
|
17564
|
+
selected_indices = self.getSelectedShotIndices()
|
|
17565
|
+
if len(selected_indices) >= 2:
|
|
17566
|
+
first_source_default = selected_indices[0] + 1
|
|
17567
|
+
last_source_default = selected_indices[-1] + 1
|
|
17568
|
+
else:
|
|
17569
|
+
first_source_default = 1
|
|
17570
|
+
last_source_default = len(self.streams)
|
|
17571
|
+
|
|
17410
17572
|
parameters = [
|
|
17411
|
-
{'label': 'First Source #', 'initial_value':
|
|
17412
|
-
{'label': 'Last Source #', 'initial_value':
|
|
17573
|
+
{'label': 'First Source #', 'initial_value': first_source_default, 'type': 'int'},
|
|
17574
|
+
{'label': 'Last Source #', 'initial_value': last_source_default, 'type': 'int'},
|
|
17413
17575
|
{'label': 'Pad at end (seconds)', 'initial_value': 0.0, 'type': 'float'}
|
|
17414
17576
|
]
|
|
17415
17577
|
|
|
@@ -17470,11 +17632,20 @@ class MainWindow(QMainWindow):
|
|
|
17470
17632
|
def batchReverseTraces(self):
|
|
17471
17633
|
"""Batch reverse trace data (flip data matrix left-to-right) while keeping headers in place"""
|
|
17472
17634
|
if self.streams:
|
|
17635
|
+
# Get selected shot indices for default values (only if 2+ shots selected)
|
|
17636
|
+
selected_indices = self.getSelectedShotIndices()
|
|
17637
|
+
if len(selected_indices) >= 2:
|
|
17638
|
+
first_source_default = selected_indices[0] + 1
|
|
17639
|
+
last_source_default = selected_indices[-1] + 1
|
|
17640
|
+
else:
|
|
17641
|
+
first_source_default = 1
|
|
17642
|
+
last_source_default = len(self.streams)
|
|
17643
|
+
|
|
17473
17644
|
n_traces = len(self.trace_position[self.currentIndex]) if self.currentIndex < len(self.trace_position) else 1
|
|
17474
17645
|
|
|
17475
17646
|
parameters = [
|
|
17476
|
-
{'label': 'First Source #', 'initial_value':
|
|
17477
|
-
{'label': 'Last Source #', 'initial_value':
|
|
17647
|
+
{'label': 'First Source #', 'initial_value': first_source_default, 'type': 'int'},
|
|
17648
|
+
{'label': 'Last Source #', 'initial_value': last_source_default, 'type': 'int'},
|
|
17478
17649
|
{'label': 'First Trace #', 'initial_value': 1, 'type': 'int'},
|
|
17479
17650
|
{'label': 'Last Trace #', 'initial_value': n_traces, 'type': 'int'}
|
|
17480
17651
|
]
|
|
@@ -17838,6 +18009,203 @@ class MainWindow(QMainWindow):
|
|
|
17838
18009
|
QMessageBox.information(self, "Stacking Complete",
|
|
17839
18010
|
f"Successfully created {stacked_shot_count} stacked shot(s).")
|
|
17840
18011
|
|
|
18012
|
+
def stackSelectedShots(self):
|
|
18013
|
+
"""
|
|
18014
|
+
Stack selected shots regardless of their source position.
|
|
18015
|
+
Creates a new stacked shot with traces summed at identical receiver positions.
|
|
18016
|
+
Uses the source position from the first selected shot.
|
|
18017
|
+
"""
|
|
18018
|
+
import obspy
|
|
18019
|
+
from obspy import Stream
|
|
18020
|
+
|
|
18021
|
+
if not self.streams:
|
|
18022
|
+
QMessageBox.information(self, "No Data", "No shots loaded. Please load shot data first.")
|
|
18023
|
+
return
|
|
18024
|
+
|
|
18025
|
+
# Get selected shot indices
|
|
18026
|
+
selected_indices = self.getSelectedShotIndices()
|
|
18027
|
+
|
|
18028
|
+
if len(selected_indices) < 2:
|
|
18029
|
+
QMessageBox.warning(self, "Insufficient Selection",
|
|
18030
|
+
"Please select at least 2 shots to stack. Use Ctrl+Click to select multiple shots.")
|
|
18031
|
+
return
|
|
18032
|
+
|
|
18033
|
+
# Build a dictionary of receiver positions across all selected shots
|
|
18034
|
+
# Key: receiver position, Value: list of (shot_idx, trace_idx) tuples
|
|
18035
|
+
receiver_traces = {}
|
|
18036
|
+
|
|
18037
|
+
for shot_idx in selected_indices:
|
|
18038
|
+
for trace_idx, receiver_pos in enumerate(self.trace_position[shot_idx]):
|
|
18039
|
+
if receiver_pos not in receiver_traces:
|
|
18040
|
+
receiver_traces[receiver_pos] = []
|
|
18041
|
+
receiver_traces[receiver_pos].append((shot_idx, trace_idx))
|
|
18042
|
+
|
|
18043
|
+
# Get unique receiver positions (sorted)
|
|
18044
|
+
receiver_positions = sorted(receiver_traces.keys())
|
|
18045
|
+
|
|
18046
|
+
if not receiver_positions:
|
|
18047
|
+
QMessageBox.warning(self, "No Receiver Positions",
|
|
18048
|
+
"Could not find any receiver positions in selected shots.")
|
|
18049
|
+
return
|
|
18050
|
+
|
|
18051
|
+
# Use source position from first selected shot
|
|
18052
|
+
first_shot_idx = selected_indices[0]
|
|
18053
|
+
source_pos = self.source_position[first_shot_idx]
|
|
18054
|
+
|
|
18055
|
+
# Show confirmation dialog
|
|
18056
|
+
shot_indices_str = ', '.join(str(i + 1) for i in selected_indices)
|
|
18057
|
+
reply = QMessageBox.question(self, "Confirm Stacking",
|
|
18058
|
+
f"Stack {len(selected_indices)} selected shot(s) ({shot_indices_str})?\n\n"
|
|
18059
|
+
f"Source position: {source_pos}m\n"
|
|
18060
|
+
f"Number of unique receiver positions: {len(receiver_positions)}\n\n"
|
|
18061
|
+
f"Traces at the same receiver position will be summed.",
|
|
18062
|
+
QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
|
|
18063
|
+
|
|
18064
|
+
if reply == QMessageBox.No:
|
|
18065
|
+
return
|
|
18066
|
+
|
|
18067
|
+
QApplication.setOverrideCursor(Qt.WaitCursor)
|
|
18068
|
+
try:
|
|
18069
|
+
# Create stacked stream
|
|
18070
|
+
stacked_stream = obspy.Stream()
|
|
18071
|
+
|
|
18072
|
+
# For each receiver position, sum all traces at that position
|
|
18073
|
+
for receiver_pos in receiver_positions:
|
|
18074
|
+
trace_list = receiver_traces[receiver_pos]
|
|
18075
|
+
|
|
18076
|
+
# Get all traces at this receiver position
|
|
18077
|
+
traces_to_sum = []
|
|
18078
|
+
for shot_idx, trace_idx in trace_list:
|
|
18079
|
+
traces_to_sum.append(self.streams[shot_idx][trace_idx])
|
|
18080
|
+
|
|
18081
|
+
# Sum the traces
|
|
18082
|
+
if traces_to_sum:
|
|
18083
|
+
summed_trace = traces_to_sum[0].copy()
|
|
18084
|
+
for trace in traces_to_sum[1:]:
|
|
18085
|
+
summed_trace.data += trace.data
|
|
18086
|
+
stacked_stream.append(summed_trace)
|
|
18087
|
+
|
|
18088
|
+
if len(stacked_stream) == 0:
|
|
18089
|
+
QMessageBox.warning(self, "No Traces", "Could not create stacked traces.")
|
|
18090
|
+
return
|
|
18091
|
+
|
|
18092
|
+
# Find the maximum existing FFID and increment
|
|
18093
|
+
max_ffid = max(self.ffid) if self.ffid else 0
|
|
18094
|
+
new_ffid = max_ffid + 1
|
|
18095
|
+
|
|
18096
|
+
# Get format from first shot
|
|
18097
|
+
format_to_use = self.input_format[first_shot_idx]
|
|
18098
|
+
|
|
18099
|
+
# Update FFID in headers for stacked stream
|
|
18100
|
+
for trace in stacked_stream:
|
|
18101
|
+
trace.stats[format_to_use].trace_header.original_field_record_number = new_ffid
|
|
18102
|
+
|
|
18103
|
+
# Append to parallel arrays
|
|
18104
|
+
stacked_shot_name = f"stacked_selected_{new_ffid}"
|
|
18105
|
+
self.fileNames.append(stacked_shot_name)
|
|
18106
|
+
self.streams.append(stacked_stream)
|
|
18107
|
+
self.input_format.append(format_to_use)
|
|
18108
|
+
|
|
18109
|
+
# Extract data from the stacked stream
|
|
18110
|
+
n_sample_stacked = len(stacked_stream[0].data)
|
|
18111
|
+
self.n_sample.append(n_sample_stacked)
|
|
18112
|
+
|
|
18113
|
+
# Get sample interval from first shot
|
|
18114
|
+
sample_interval_stacked = self.sample_interval[first_shot_idx]
|
|
18115
|
+
self.sample_interval.append(sample_interval_stacked)
|
|
18116
|
+
|
|
18117
|
+
# Get delay from first shot
|
|
18118
|
+
delay_stacked = self.delay[first_shot_idx]
|
|
18119
|
+
self.delay.append(delay_stacked)
|
|
18120
|
+
|
|
18121
|
+
# Generate time array
|
|
18122
|
+
time_stacked = np.arange(n_sample_stacked) * sample_interval_stacked + delay_stacked
|
|
18123
|
+
self.time.append(time_stacked)
|
|
18124
|
+
|
|
18125
|
+
# Get record length
|
|
18126
|
+
record_length_stacked = self.record_length[first_shot_idx]
|
|
18127
|
+
self.record_length.append(record_length_stacked)
|
|
18128
|
+
|
|
18129
|
+
# FFID
|
|
18130
|
+
self.ffid.append(new_ffid)
|
|
18131
|
+
|
|
18132
|
+
# Source position (from first selected shot)
|
|
18133
|
+
self.source_position.append(source_pos)
|
|
18134
|
+
|
|
18135
|
+
# Trace numbering
|
|
18136
|
+
shot_trace_numbers = [i + 1 for i in range(len(stacked_stream))]
|
|
18137
|
+
self.shot_trace_number.append(shot_trace_numbers)
|
|
18138
|
+
|
|
18139
|
+
# Trace positions (receiver positions, sorted)
|
|
18140
|
+
self.trace_position.append(receiver_positions)
|
|
18141
|
+
|
|
18142
|
+
# File trace numbers (sequential)
|
|
18143
|
+
file_trace_numbers = list(range(1, len(stacked_stream) + 1))
|
|
18144
|
+
self.file_trace_number.append(file_trace_numbers)
|
|
18145
|
+
|
|
18146
|
+
# Initialize unique_trace_number (will be computed later)
|
|
18147
|
+
self.unique_trace_number.append(None)
|
|
18148
|
+
|
|
18149
|
+
# Trace elevations (from first occurrence of each position)
|
|
18150
|
+
trace_elevations_stacked = []
|
|
18151
|
+
for receiver_pos in receiver_positions:
|
|
18152
|
+
# Find first occurrence of this position
|
|
18153
|
+
for shot_idx, trace_idx in receiver_traces[receiver_pos]:
|
|
18154
|
+
trace_elevations_stacked.append(self.trace_elevation[shot_idx][trace_idx])
|
|
18155
|
+
break
|
|
18156
|
+
self.trace_elevation.append(trace_elevations_stacked)
|
|
18157
|
+
|
|
18158
|
+
# Source elevation (from first shot)
|
|
18159
|
+
self.source_elevation.append(self.source_elevation[first_shot_idx])
|
|
18160
|
+
|
|
18161
|
+
# Offset (distance from source to each receiver)
|
|
18162
|
+
offsets_stacked = [abs(rp - source_pos) for rp in receiver_positions]
|
|
18163
|
+
self.offset.append(offsets_stacked)
|
|
18164
|
+
|
|
18165
|
+
# Initialize picks, errors, and UI items as None
|
|
18166
|
+
self.picks.append(None)
|
|
18167
|
+
self.error.append(None)
|
|
18168
|
+
self.pickSeismoItems.append(None)
|
|
18169
|
+
self.pickLayoutItems.append(None)
|
|
18170
|
+
self.airWaveItems.append(None)
|
|
18171
|
+
|
|
18172
|
+
# Update global trace numbering
|
|
18173
|
+
self._updateAllUniqueTraceNumbers()
|
|
18174
|
+
|
|
18175
|
+
# Sync headers
|
|
18176
|
+
self.syncHeadersToStreams(len(self.streams) - 1)
|
|
18177
|
+
self.headers_modified = True
|
|
18178
|
+
|
|
18179
|
+
# Update display
|
|
18180
|
+
self.updateFileListDisplay()
|
|
18181
|
+
self.updatePlots()
|
|
18182
|
+
|
|
18183
|
+
# Ask if user wants to remove the selected shots
|
|
18184
|
+
reply = QMessageBox.question(self, "Remove Selected Shots",
|
|
18185
|
+
"Remove the selected shots that were used in stacking?",
|
|
18186
|
+
QMessageBox.Yes | QMessageBox.No)
|
|
18187
|
+
|
|
18188
|
+
if reply == QMessageBox.Yes:
|
|
18189
|
+
# Remove shots in reverse order to preserve indices
|
|
18190
|
+
for idx in reversed(selected_indices):
|
|
18191
|
+
for attr in self.attributes_to_initialize:
|
|
18192
|
+
getattr(self, attr).pop(idx)
|
|
18193
|
+
|
|
18194
|
+
# Update display
|
|
18195
|
+
self.updateFileListDisplay()
|
|
18196
|
+
if self.streams:
|
|
18197
|
+
self.currentIndex = min(self.currentIndex, len(self.streams) - 1)
|
|
18198
|
+
self.fileListWidget.setCurrentRow(self.currentIndex)
|
|
18199
|
+
self.updatePlots()
|
|
18200
|
+
|
|
18201
|
+
QMessageBox.information(self, "Stacking Complete",
|
|
18202
|
+
f"Successfully stacked {len(selected_indices)} shot(s) into FFID {new_ffid}.\n"
|
|
18203
|
+
f"Source position: {source_pos}m\n"
|
|
18204
|
+
f"Number of traces: {len(receiver_positions)}")
|
|
18205
|
+
|
|
18206
|
+
finally:
|
|
18207
|
+
QApplication.restoreOverrideCursor()
|
|
18208
|
+
|
|
17841
18209
|
#######################################
|
|
17842
18210
|
# Set parameters functions
|
|
17843
18211
|
#######################################
|
pyckster/obspy_utils.py
CHANGED
|
@@ -2,6 +2,7 @@ import os
|
|
|
2
2
|
import re
|
|
3
3
|
import numpy as np
|
|
4
4
|
import obspy
|
|
5
|
+
import warnings
|
|
5
6
|
|
|
6
7
|
#######################################
|
|
7
8
|
# Obspy functions
|
|
@@ -63,39 +64,49 @@ def read_seismic_file(seismic_file, separate_sources=False):
|
|
|
63
64
|
stream : obspy.Stream
|
|
64
65
|
The stream object containing the seismic data.
|
|
65
66
|
'''
|
|
66
|
-
#
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
'.
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
67
|
+
# Suppress expected warnings from ObsPy's SEG2 reader
|
|
68
|
+
# These warnings are expected and handled by our code:
|
|
69
|
+
# 1. DELAY field warning - we manually extract and apply the delay
|
|
70
|
+
# 2. Custom header warning - we handle custom SEG2 headers appropriately
|
|
71
|
+
# 3. Creating trace header - expected during SEG2 to SEGY conversion
|
|
72
|
+
with warnings.catch_warnings():
|
|
73
|
+
warnings.filterwarnings('ignore', message='.*DELAY.*', category=UserWarning)
|
|
74
|
+
warnings.filterwarnings('ignore', message='.*custom defined SEG2 header.*', category=UserWarning)
|
|
75
|
+
warnings.filterwarnings('ignore', message='CREATING TRACE HEADER', category=UserWarning)
|
|
76
|
+
|
|
77
|
+
# Validate .dat files are actually binary, not ASCII
|
|
78
|
+
file_ext = os.path.splitext(seismic_file)[1].lower()
|
|
79
|
+
if file_ext == '.dat':
|
|
80
|
+
if not is_valid_binary_seismic_file(seismic_file):
|
|
81
|
+
raise ValueError(
|
|
82
|
+
f"File '{os.path.basename(seismic_file)}' appears to be an ASCII text file, "
|
|
83
|
+
f"not a binary Seg2 seismic file. Please verify the file format."
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
# Determine format based on file extension
|
|
87
|
+
format_map = {
|
|
88
|
+
'.sgy': 'SEGY',
|
|
89
|
+
'.seg': 'SEGY',
|
|
90
|
+
'.segy': 'SEGY',
|
|
91
|
+
'.su': 'SU',
|
|
92
|
+
'.seg2': 'SEG2',
|
|
93
|
+
'.sg2': 'SEG2',
|
|
94
|
+
'.dat': 'SEG2', # .dat files are typically SEG2
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
# Get format from extension, default to None (let obspy auto-detect)
|
|
98
|
+
file_format = format_map.get(file_ext, None)
|
|
99
|
+
|
|
100
|
+
# Read the seismic file with explicit format if known
|
|
101
|
+
if file_format:
|
|
102
|
+
stream = obspy.read(seismic_file, format=file_format, unpack_trace_headers=True)
|
|
103
|
+
else:
|
|
104
|
+
stream = obspy.read(seismic_file, unpack_trace_headers=True)
|
|
94
105
|
|
|
95
106
|
input_format = check_format(stream)
|
|
96
107
|
|
|
97
108
|
if input_format == 'seg2':
|
|
98
|
-
# Preserve original SEG2 coordinate information before conversion
|
|
109
|
+
# Preserve original SEG2 coordinate information and delay before conversion
|
|
99
110
|
original_seg2_coords = []
|
|
100
111
|
for trace_index, trace in enumerate(stream):
|
|
101
112
|
coord_info = {
|
|
@@ -105,13 +116,24 @@ def read_seismic_file(seismic_file, separate_sources=False):
|
|
|
105
116
|
'source_x': 0.0,
|
|
106
117
|
'source_y': 0.0,
|
|
107
118
|
'source_z': 0.0,
|
|
119
|
+
'delay': 0.0,
|
|
108
120
|
'trace_index': trace_index
|
|
109
121
|
}
|
|
110
122
|
|
|
111
|
-
# Extract coordinates from SEG2 headers with multiple field name variations
|
|
123
|
+
# Extract coordinates and delay from SEG2 headers with multiple field name variations
|
|
112
124
|
if hasattr(trace.stats, 'seg2'):
|
|
113
125
|
seg2 = trace.stats.seg2
|
|
114
126
|
|
|
127
|
+
# Extract DELAY from SEG2 headers
|
|
128
|
+
delay_fields = ['DELAY', 'DELAY_TIME', 'RECORDING_DELAY']
|
|
129
|
+
for field in delay_fields:
|
|
130
|
+
if hasattr(seg2, field):
|
|
131
|
+
try:
|
|
132
|
+
coord_info['delay'] = float(getattr(seg2, field))
|
|
133
|
+
break
|
|
134
|
+
except (ValueError, TypeError):
|
|
135
|
+
continue
|
|
136
|
+
|
|
115
137
|
# Try different field names for receiver location
|
|
116
138
|
receiver_location_fields = ['RECEIVER_LOCATION', 'RECEIVER_LOCATION_X', 'RECEIVER_X', 'REC_X']
|
|
117
139
|
for field in receiver_location_fields:
|
|
@@ -170,8 +192,11 @@ def read_seismic_file(seismic_file, separate_sources=False):
|
|
|
170
192
|
else:
|
|
171
193
|
ffid = 1
|
|
172
194
|
|
|
173
|
-
|
|
174
|
-
|
|
195
|
+
# Suppress warnings during SEG2 to SEGY conversion
|
|
196
|
+
with warnings.catch_warnings():
|
|
197
|
+
warnings.filterwarnings('ignore', message='CREATING TRACE HEADER', category=UserWarning)
|
|
198
|
+
stream.write('tmp.sgy',format='SEGY',data_encoding=5, byteorder='>')
|
|
199
|
+
stream = obspy.read('tmp.sgy',unpack_trace_headers=True)
|
|
175
200
|
os.remove('tmp.sgy')
|
|
176
201
|
|
|
177
202
|
# Calculate coordinate scalar for preserved coordinates
|
|
@@ -204,6 +229,8 @@ def read_seismic_file(seismic_file, separate_sources=False):
|
|
|
204
229
|
trace.stats[input_format].trace_header.source_coordinate_x = int(coord_info['source_x'] * coordinate_scalar)
|
|
205
230
|
trace.stats[input_format].trace_header.source_coordinate_y = int(coord_info['source_y'] * coordinate_scalar)
|
|
206
231
|
trace.stats[input_format].trace_header.surface_elevation_at_source = int(coord_info['source_z'] * coordinate_scalar)
|
|
232
|
+
# Apply delay from SEG2 header (convert to milliseconds for SEGY)
|
|
233
|
+
trace.stats[input_format].trace_header.delay_recording_time = int(coord_info['delay'] * 1000)
|
|
207
234
|
|
|
208
235
|
if separate_sources:
|
|
209
236
|
stream = separate_streams(stream)
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
pyckster/__init__.py,sha256=
|
|
1
|
+
pyckster/__init__.py,sha256=hyEyaD3gWhebOlI1NcoMePXDldgUNNoe7mZ-l0lIm-Y,905
|
|
2
2
|
pyckster/__main__.py,sha256=zv3AGVKorKo2tgWOEIcVnkDbp15eepSqka3IoWH_adU,406
|
|
3
3
|
pyckster/auto_picking.py,sha256=fyZiOj0Ib-SB_oxsKnUszECHbOjo4JE23JVQILGYZco,12754
|
|
4
4
|
pyckster/bayesian_inversion.py,sha256=kdnKOlAZ0JlYLipuFDHlwS7dU8LtI-0aMb90bYpEHhE,163523
|
|
5
|
-
pyckster/core.py,sha256=
|
|
5
|
+
pyckster/core.py,sha256=HDJz6UB2TaGM-i68BRgb9X2poM3CJQDiOE3Is_IwG7w,1268015
|
|
6
6
|
pyckster/dispersion_stack_viewer.py,sha256=7Dh2e1tSct062D7Qh6nNrMdJcqKWcJvDIv84V8sC6C8,12645
|
|
7
7
|
pyckster/inversion_app.py,sha256=ovM44oYBFsvfKxO7rjjThUhkJnLDLZZ0R6ZVp-5r66E,60676
|
|
8
8
|
pyckster/inversion_manager.py,sha256=P8i1fqUJKMWkd-9PoDmNtmQuKglGKTeSuptUUA57D-8,15393
|
|
9
9
|
pyckster/inversion_visualizer.py,sha256=vfKZIoJzKawbaEv29NsYYIGnWLDQCGef5bM2vY1aCBo,22135
|
|
10
10
|
pyckster/ipython_console.py,sha256=tZyyoiXCjCl7ozxOj_h-YR4eGjoC4kpKe7nZ48eUAJc,9313
|
|
11
11
|
pyckster/mpl_export.py,sha256=_WqPo9l9ABiSoU0ukLfm4caGV1-FKKbXjt8SoBHTR30,12346
|
|
12
|
-
pyckster/obspy_utils.py,sha256=
|
|
12
|
+
pyckster/obspy_utils.py,sha256=Rb0-HAZpil7LxDOMtoKqxlSYL71LVe9rAMrT0PRHXbU,37132
|
|
13
13
|
pyckster/pick_io.py,sha256=r7QoRCA2zaGeZlFKuAJ86KnAH_mh_l4qGvP02Q0mwVA,36001
|
|
14
14
|
pyckster/pyqtgraph_utils.py,sha256=PAeE3n_wz7skHOC5eLnkFczbie7diVH1xvuL8jtJ4T8,6049
|
|
15
15
|
pyckster/surface_wave_analysis.py,sha256=97BrDA-n5AZp89NdxQ2ekZPaCErMc7v8C6GmD5KTi-4,102695
|
|
@@ -17,9 +17,9 @@ pyckster/surface_wave_profiling.py,sha256=L9KidhKmfGvVoPZjf6us3c49VB7VPB_VcsDqRx
|
|
|
17
17
|
pyckster/sw_utils.py,sha256=-2CpQ9BkmUHaMBrNy2qXx1R-g9qPX8D9igKi_G-iRHE,13213
|
|
18
18
|
pyckster/tab_factory.py,sha256=NlCIC6F8BrEu7a8BYOJJdWy5ftpX_zKDLj7SbcwBbh8,14519
|
|
19
19
|
pyckster/visualization_utils.py,sha256=bgODn21NAQx1FOMPj91kdDd0szKOgUyfZ3cQlyu2PF8,47947
|
|
20
|
-
pyckster-26.2.
|
|
21
|
-
pyckster-26.2.
|
|
22
|
-
pyckster-26.2.
|
|
23
|
-
pyckster-26.2.
|
|
24
|
-
pyckster-26.2.
|
|
25
|
-
pyckster-26.2.
|
|
20
|
+
pyckster-26.2.4.dist-info/licenses/LICENCE,sha256=-uaAIm20JrJKoMdCdn2GlFQfNU4fbsHWK3eh4kIQ_Ec,35143
|
|
21
|
+
pyckster-26.2.4.dist-info/METADATA,sha256=TKeHttAjV2XDga_kdw2glW3b3c6GMP2GczZCK2L0tVk,4567
|
|
22
|
+
pyckster-26.2.4.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
23
|
+
pyckster-26.2.4.dist-info/entry_points.txt,sha256=yrOQx1wHi84rbxX_ZYtYaVcK3EeuRhHRQDZRc8mB0NI,100
|
|
24
|
+
pyckster-26.2.4.dist-info/top_level.txt,sha256=eaihhwhEmlysgdZE4HmELFdSUwlXcMv90YorkjOXujQ,9
|
|
25
|
+
pyckster-26.2.4.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|