dssrr 0.1.0__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 (98) hide show
  1. dssrr-0.1.0/LICENSE +21 -0
  2. dssrr-0.1.0/MANIFEST.in +41 -0
  3. dssrr-0.1.0/PKG-INFO +483 -0
  4. dssrr-0.1.0/README.md +434 -0
  5. dssrr-0.1.0/docs/CLI.md +167 -0
  6. dssrr-0.1.0/docs/images/icon.png +0 -0
  7. dssrr-0.1.0/docs/images/pipeline.svg +78 -0
  8. dssrr-0.1.0/dssrr/__init__.py +231 -0
  9. dssrr-0.1.0/dssrr/cli.py +305 -0
  10. dssrr-0.1.0/dssrr/config.py +175 -0
  11. dssrr-0.1.0/dssrr/core.py +553 -0
  12. dssrr-0.1.0/dssrr/discovery.py +204 -0
  13. dssrr-0.1.0/dssrr/gui/__init__.py +33 -0
  14. dssrr-0.1.0/dssrr/gui/__main__.py +21 -0
  15. dssrr-0.1.0/dssrr/gui/batch_repair.py +782 -0
  16. dssrr-0.1.0/dssrr/gui/depulse_dialog.py +2718 -0
  17. dssrr-0.1.0/dssrr/gui/export_report_dialog.py +658 -0
  18. dssrr-0.1.0/dssrr/gui/i18n.py +413 -0
  19. dssrr-0.1.0/dssrr/gui/icons/__init__.py +39 -0
  20. dssrr-0.1.0/dssrr/gui/icons/dssrr-128.png +0 -0
  21. dssrr-0.1.0/dssrr/gui/icons/dssrr-16.png +0 -0
  22. dssrr-0.1.0/dssrr/gui/icons/dssrr-24.png +0 -0
  23. dssrr-0.1.0/dssrr/gui/icons/dssrr-256.png +0 -0
  24. dssrr-0.1.0/dssrr/gui/icons/dssrr-32.png +0 -0
  25. dssrr-0.1.0/dssrr/gui/icons/dssrr-48.png +0 -0
  26. dssrr-0.1.0/dssrr/gui/icons/dssrr-64.png +0 -0
  27. dssrr-0.1.0/dssrr/gui/icons/dssrr.ico +0 -0
  28. dssrr-0.1.0/dssrr/gui/icons/dssrr.png +0 -0
  29. dssrr-0.1.0/dssrr/gui/icons/dssrr.svg +24 -0
  30. dssrr-0.1.0/dssrr/gui/icons/make_icon.py +209 -0
  31. dssrr-0.1.0/dssrr/gui/main.py +846 -0
  32. dssrr-0.1.0/dssrr/gui/manual_repair.py +1077 -0
  33. dssrr-0.1.0/dssrr/gui/nav_hint.py +182 -0
  34. dssrr-0.1.0/dssrr/gui/plot_text_editor.py +505 -0
  35. dssrr-0.1.0/dssrr/gui/translations_zh.py +665 -0
  36. dssrr-0.1.0/dssrr/gui/viewer.py +3008 -0
  37. dssrr-0.1.0/dssrr/i18n.py +122 -0
  38. dssrr-0.1.0/dssrr/paths.py +121 -0
  39. dssrr-0.1.0/dssrr/repair_lib/__init__.py +38 -0
  40. dssrr-0.1.0/dssrr/repair_lib/anomaly_repair/__init__.py +55 -0
  41. dssrr-0.1.0/dssrr/repair_lib/anomaly_repair/config.py +26 -0
  42. dssrr-0.1.0/dssrr/repair_lib/anomaly_repair/layered_reference_spectrum.py +506 -0
  43. dssrr-0.1.0/dssrr/repair_lib/anomaly_repair/moonquake_protected_spike.py +371 -0
  44. dssrr-0.1.0/dssrr/repair_lib/anomaly_repair/reference_spectrum.py +42 -0
  45. dssrr-0.1.0/dssrr/repair_lib/anomaly_repair/spectral.py +22 -0
  46. dssrr-0.1.0/dssrr/repair_lib/anomaly_repair/synthesizer.py +29 -0
  47. dssrr-0.1.0/dssrr/repair_lib/anomaly_repair/verifier.py +21 -0
  48. dssrr-0.1.0/dssrr/repair_lib/apollo_catalog/gagnepian_2006_catalog.csv +59 -0
  49. dssrr-0.1.0/dssrr/repair_lib/apollo_catalog/lognonne_2003_catalog.csv +60 -0
  50. dssrr-0.1.0/dssrr/repair_lib/depulse_advanced.py +973 -0
  51. dssrr-0.1.0/dssrr/repair_lib/depulse_nn.py +187 -0
  52. dssrr-0.1.0/dssrr/repair_lib/e7_detector.py +622 -0
  53. dssrr-0.1.0/dssrr/repair_lib/e7_repair_build.py +1719 -0
  54. dssrr-0.1.0/dssrr/repair_lib/moonquake_catalog.json +9515 -0
  55. dssrr-0.1.0/dssrr/repair_lib/repair_engine.py +673 -0
  56. dssrr-0.1.0/dssrr/repair_lib/repair_settings.py +139 -0
  57. dssrr-0.1.0/dssrr/repair_lib/report_plot.py +913 -0
  58. dssrr-0.1.0/dssrr/spectral.py +209 -0
  59. dssrr-0.1.0/dssrr/synthesizer.py +716 -0
  60. dssrr-0.1.0/dssrr/verifier.py +309 -0
  61. dssrr-0.1.0/dssrr.egg-info/PKG-INFO +483 -0
  62. dssrr-0.1.0/dssrr.egg-info/SOURCES.txt +96 -0
  63. dssrr-0.1.0/dssrr.egg-info/dependency_links.txt +1 -0
  64. dssrr-0.1.0/dssrr.egg-info/entry_points.txt +3 -0
  65. dssrr-0.1.0/dssrr.egg-info/requires.txt +26 -0
  66. dssrr-0.1.0/dssrr.egg-info/top_level.txt +1 -0
  67. dssrr-0.1.0/examples/01_quickstart.py +149 -0
  68. dssrr-0.1.0/examples/02_long_gap_baseline.py +158 -0
  69. dssrr-0.1.0/examples/03_real_apollo_mseed.py +198 -0
  70. dssrr-0.1.0/examples/04_report_plot.py +133 -0
  71. dssrr-0.1.0/examples/05_batch_cli.py +136 -0
  72. dssrr-0.1.0/examples/README.md +46 -0
  73. dssrr-0.1.0/examples/data/01/XA.S12.19760101.mseed +0 -0
  74. dssrr-0.1.0/examples/data/01/XA.S12.19760102.mseed +0 -0
  75. dssrr-0.1.0/examples/data/01/XA.S12.19760103.mseed +0 -0
  76. dssrr-0.1.0/examples/data/01/XA.S12.19760104.mseed +0 -0
  77. dssrr-0.1.0/examples/data/01/XA.S12.19760105.mseed +0 -0
  78. dssrr-0.1.0/examples/data/01/XA.S12.19760106.mseed +0 -0
  79. dssrr-0.1.0/examples/data/01/XA.S12.19760107.mseed +0 -0
  80. dssrr-0.1.0/examples/data/01/XA.S12.19760108.mseed +0 -0
  81. dssrr-0.1.0/examples/data/01/XA.S12.19760109.mseed +0 -0
  82. dssrr-0.1.0/examples/data/01/XA.S12.19760110.mseed +0 -0
  83. dssrr-0.1.0/examples/data/01/XA.S12.19760111.mseed +0 -0
  84. dssrr-0.1.0/examples/data/01/XA.S12.19760112.mseed +0 -0
  85. dssrr-0.1.0/examples/data/01/XA.S12.19760113.mseed +0 -0
  86. dssrr-0.1.0/examples/data/README.md +72 -0
  87. dssrr-0.1.0/examples/data/XA.S12.01.MHZ.19760113_070152-19760113_190149.mseed +0 -0
  88. dssrr-0.1.0/examples/data/XA.S15.01.MHZ.19760113_070152-19760113_190149.mseed +0 -0
  89. dssrr-0.1.0/examples/data/XA.S16.01.MHZ.19760113_070152-19760113_190149.mseed +0 -0
  90. dssrr-0.1.0/pyproject.toml +110 -0
  91. dssrr-0.1.0/setup.cfg +4 -0
  92. dssrr-0.1.0/setup.py +11 -0
  93. dssrr-0.1.0/tests/test_basic.py +62 -0
  94. dssrr-0.1.0/tests/test_gui_i18n.py +379 -0
  95. dssrr-0.1.0/tests/test_language_defaults.py +182 -0
  96. dssrr-0.1.0/tests/test_repair_engine.py +111 -0
  97. dssrr-0.1.0/tests/test_resolve_paths.py +179 -0
  98. dssrr-0.1.0/tests/test_single_source_of_truth.py +95 -0
dssrr-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Shipeng Yu
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,41 @@
1
+ # ---------------------------------------------------------------------------
2
+ # MANIFEST.in — what goes into the *source* distribution (sdist).
3
+ #
4
+ # The wheel is governed by [tool.setuptools.package-data] in pyproject.toml and
5
+ # only ever contains the importable ``dssrr`` package. The sdist is the
6
+ # "everything you need to reproduce the work" artefact: source, tests,
7
+ # documentation, runnable examples and their fixtures.
8
+ # ---------------------------------------------------------------------------
9
+
10
+ # Licence + top-level project documents
11
+ include LICENSE
12
+ include README.md
13
+ include pyproject.toml
14
+ include setup.py
15
+
16
+ # Package source and the data files that ship inside the installed package
17
+ # (GUI icons, moonquake catalogue). ``recursive-include`` covers subpackages
18
+ # such as dssrr.gui and dssrr.repair_lib.
19
+ recursive-include dssrr *.py
20
+ recursive-include dssrr *.json *.csv *.svg *.png *.ico
21
+
22
+ # Human-facing documentation (CLI guide, pipeline figure)
23
+ recursive-include docs *.md *.svg *.png
24
+
25
+ # Runnable examples and the Apollo MiniSEED fixtures they run against.
26
+ # The fixtures live in examples/ only — they are never installed.
27
+ recursive-include examples *.py *.md
28
+ recursive-include examples/data *.mseed *.md
29
+
30
+ # Regression test suite
31
+ recursive-include tests *.py
32
+
33
+ # --- Never ship build artefacts, caches or runtime output -------------------
34
+ global-exclude *.py[cod]
35
+ global-exclude __pycache__
36
+ prune examples/data_repaired
37
+ prune examples/__pycache__
38
+ prune tests/__pycache__
39
+ prune catalogs
40
+ prune .vscode
41
+ prune .pytest_cache
dssrr-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,483 @@
1
+ Metadata-Version: 2.4
2
+ Name: dssrr
3
+ Version: 0.1.0
4
+ Summary: Dual-Sided Spectral Reference Reconstruction: training-free long-gap repair for planetary/seismic time series
5
+ Author-email: Shipeng Yu <ysp@cug.edu.cn>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/YSP0Github/DSSRR
8
+ Project-URL: Documentation, https://github.com/YSP0Github/DSSRR#readme
9
+ Project-URL: Source Code, https://github.com/YSP0Github/DSSRR
10
+ Project-URL: Bug Tracker, https://github.com/YSP0Github/DSSRR/issues
11
+ Keywords: seismology,lunar seismology,apollo,signal processing,gap filling,spectral reconstruction,planetary science,time series,artifact removal
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Intended Audience :: Science/Research
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.9
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Topic :: Scientific/Engineering
21
+ Classifier: Topic :: Scientific/Engineering :: Astronomy
22
+ Classifier: Topic :: Scientific/Engineering :: Physics
23
+ Classifier: Topic :: Scientific/Engineering :: Information Analysis
24
+ Requires-Python: >=3.9
25
+ Description-Content-Type: text/markdown
26
+ License-File: LICENSE
27
+ Requires-Dist: numpy>=1.21
28
+ Requires-Dist: scipy>=1.7
29
+ Provides-Extra: gui
30
+ Requires-Dist: PyQt5>=5.15; extra == "gui"
31
+ Requires-Dist: matplotlib>=3.5; extra == "gui"
32
+ Requires-Dist: obspy>=1.4; extra == "gui"
33
+ Requires-Dist: pandas>=1.3; extra == "gui"
34
+ Provides-Extra: batch
35
+ Requires-Dist: obspy>=1.4; extra == "batch"
36
+ Requires-Dist: pandas>=1.3; extra == "batch"
37
+ Provides-Extra: nn
38
+ Requires-Dist: torch>=1.12; extra == "nn"
39
+ Provides-Extra: dev
40
+ Requires-Dist: pytest>=7; extra == "dev"
41
+ Requires-Dist: build>=0.10; extra == "dev"
42
+ Requires-Dist: twine>=4.0; extra == "dev"
43
+ Provides-Extra: all
44
+ Requires-Dist: PyQt5>=5.15; extra == "all"
45
+ Requires-Dist: matplotlib>=3.5; extra == "all"
46
+ Requires-Dist: obspy>=1.4; extra == "all"
47
+ Requires-Dist: pandas>=1.3; extra == "all"
48
+ Dynamic: license-file
49
+
50
+ <p align="center">
51
+ <img src="dssrr/gui/icons/dssrr.png" width="120" alt="DSSRR">
52
+ </p>
53
+
54
+ <h1 align="center">DSSRR &mdash; Dual-Sided Spectral Reference Reconstruction</h1>
55
+
56
+ <p align="center">
57
+ <b>Training-free, statistically faithful long-gap repair for planetary and seismic time series.</b>
58
+ </p>
59
+
60
+ <p align="center">
61
+ <a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License: MIT"></a>
62
+ <img src="https://img.shields.io/badge/python-3.9%2B-blue.svg" alt="Python 3.9+">
63
+ <img src="https://img.shields.io/badge/core%20deps-numpy%20%2B%20scipy-success.svg" alt="numpy + scipy">
64
+ </p>
65
+
66
+ ---
67
+
68
+ DSSRR fills corrupted or missing intervals in a time series by fusing power
69
+ spectral density (PSD) estimates taken from the healthy data on **both** sides
70
+ of the gap, synthesising a replacement with realistic waveform texture, and
71
+ blending it seamlessly into the surrounding trace.
72
+
73
+ No training data, no model fitting, no learned weights. It stays stable for
74
+ gaps from a few seconds to over **90 minutes**, where interpolation, SSA and
75
+ plain FFT extrapolation degrade sharply.
76
+
77
+ The method was developed for **Apollo lunar seismic (PSE) records** and ships
78
+ with the detection / batch-repair pipeline and the desktop tooling used to
79
+ produce the results in the accompanying paper.
80
+
81
+ ## Table of contents
82
+
83
+ - [How it works](#how-it-works)
84
+ - [Installation](#installation)
85
+ - [Quick start](#quick-start)
86
+ - [Examples](#examples)
87
+ - [Desktop GUI](#desktop-gui)
88
+ - [Batch repair from the command line](#batch-repair-from-the-command-line)
89
+ - [Parameters](#parameters)
90
+ - [Key features](#key-features)
91
+ - [Limitations](#limitations)
92
+ - [Repository layout](#repository-layout)
93
+ - [Sample data, provenance and acknowledgements](#sample-data-provenance-and-acknowledgements)
94
+ - [FAQ](#faq)
95
+ - [Citation](#citation)
96
+ - [License](#license)
97
+
98
+ ## How it works
99
+
100
+ ![DSSRR pipeline](docs/images/pipeline.svg)
101
+
102
+ 1. **Locate the anomaly core.** You pass inclusive sample indices `[start, end]`.
103
+ 2. **Extract two reference windows.** One before and one after the gap, each of
104
+ length `L = clip(anomaly/2, 120 s, 600 s)` by default
105
+ (see [`auto_reference_length`](#reference-length)). A safety gap (default
106
+ 1 s) separates the anomaly from each reference so the anomaly never leaks in.
107
+ 3. **Fuse a target PSD.** Welch estimates from both references are combined
108
+ into a single target spectrum, so a non-stationary neighbour on one side
109
+ cannot dominate.
110
+ 4. **Synthesise the replacement.** Reference blocks with randomised phase are
111
+ overlap-added and rescaled to match the target spectrum's energy &mdash; this
112
+ is what gives the fill realistic texture instead of a smooth line.
113
+ 5. **Align and blend.** The low-frequency trend is bridged across the gap and
114
+ the edges are cross-faded so no step is visible at the joins.
115
+ 6. **Optional quantisation.** For raw integer counts (e.g. the Apollo archive)
116
+ the repaired samples can be rounded back onto the original integer grid.
117
+ 7. **Verify.** Spectral, envelope, DC, autocorrelation and energy diagnostics
118
+ are returned alongside the data.
119
+ 8. **Return.** A repaired copy plus a full report. **Every sample outside the
120
+ anomaly interval is copied through bit-identically.**
121
+
122
+ > **The one thing DSSRR cannot do:** recover the original phase inside the gap.
123
+ > It reproduces second-order statistics (PSD, autocorrelation, energy
124
+ > distribution). A one-off transient that happened during the dropout is
125
+ > unrecoverable &mdash; by any method.
126
+
127
+ ## Installation
128
+
129
+ ```bash
130
+ pip install dssrr
131
+ ```
132
+
133
+ The scientific core needs only **numpy + scipy**. Everything else is an
134
+ optional extra:
135
+
136
+ | Extra | Adds | For |
137
+ |---|---|---|
138
+ | *(none)* | numpy, scipy | library + CLI batch repair |
139
+ | `dssrr[batch]` | obspy, pandas | reading MiniSEED, writing catalog CSVs |
140
+ | `dssrr[gui]` | PyQt5, matplotlib, obspy, pandas | the desktop application |
141
+ | `dssrr[nn]` | torch | neural-network inpainting (optional repair method) |
142
+ | `dssrr[dev]` | pytest + tooling | running the test suite |
143
+ | `dssrr[all]` | gui + batch extras | everything except the optional torch backend |
144
+
145
+ ```bash
146
+ pip install "dssrr[gui]" # desktop app
147
+ pip install "dssrr[all]" # everything
148
+ ```
149
+
150
+ From a source checkout:
151
+
152
+ ```bash
153
+ git clone https://github.com/YSP0Github/DSSRR.git dssrr && cd dssrr
154
+ pip install -e ".[gui]"
155
+ ```
156
+
157
+ ## Quick start
158
+
159
+ ```python
160
+ import numpy as np
161
+ from dssrr import DSSRR, auto_reference_length
162
+
163
+ sr = 6.625 # Apollo long-period sampling rate
164
+ data = ... # 1-D array, your trace
165
+
166
+ start, end = 12_000, 16_000 # inclusive anomaly sample indices
167
+
168
+ # Pick a reference window from the anomaly length (120-600 s protocol)
169
+ ref = auto_reference_length(end - start + 1, sr)
170
+
171
+ model = DSSRR(sr=sr, reference_sec=ref, seed=42)
172
+ repaired = model.repair(data, start, end) # numpy array, same shape
173
+
174
+ # Or get the full provenance / quality report too:
175
+ repaired, report = model.repair_with_report(data, start, end, random_seed=42)
176
+ print(report["verification"]["energy_ratio"])
177
+ ```
178
+
179
+ Two guarantees you can rely on:
180
+
181
+ ```python
182
+ assert repaired.shape == data.shape
183
+ assert np.array_equal(repaired[:start], data[:start]) # outside untouched
184
+ assert np.array_equal(repaired[end + 1:], data[end + 1:])
185
+ ```
186
+
187
+ ### Integer-count data (`quantize`)
188
+
189
+ By default the repaired segment stays **floating point**, which is correct for
190
+ physical-unit traces (velocity / displacement / acceleration). If your input is
191
+ **raw integer counts** &mdash; such as Apollo archival data on a 10-bit grid &mdash;
192
+ enable `quantize` so the repaired samples land on the same discrete grid as the
193
+ rest of the record:
194
+
195
+ ```python
196
+ model = DSSRR(sr=sr, reference_sec=300, quantize=True)
197
+ repaired = model.repair(data, start, end) # integer output
198
+ repaired = model.repair(data, start, end, quantize=True) # or per call
199
+ ```
200
+
201
+ Rounding is `np.rint` (round-half-to-even) and is applied **only inside the
202
+ anomaly interval**; everything outside stays bit-identical.
203
+
204
+ ### Reference length
205
+
206
+ ```python
207
+ from dssrr import auto_reference_length
208
+ ref_sec = auto_reference_length(anomaly_samples, sr) # clip(anom/2, 120, 600)
209
+ ```
210
+
211
+ Half the anomaly duration is enough for a stable spectral estimate; the bounds
212
+ keep the window short enough to stay locally stationary.
213
+
214
+ ## Examples
215
+
216
+ Five runnable, heavily commented scripts live in [`examples/`](examples/) &mdash;
217
+ see [`examples/README.md`](examples/README.md) for the full index.
218
+
219
+ | Script | What it shows |
220
+ |---|---|
221
+ | [`01_quickstart.py`](examples/01_quickstart.py) | Smallest possible call; the two invariants; the report dict |
222
+ | [`02_long_gap_baseline.py`](examples/02_long_gap_baseline.py) | DSSRR vs linear interpolation vs FFT extrapolation on a 10-min gap |
223
+ | [`03_real_apollo_mseed.py`](examples/03_real_apollo_mseed.py) | Repair a real Apollo record, scored against the held-out truth |
224
+ | [`04_report_plot.py`](examples/04_report_plot.py) | Publication-style repair report figure |
225
+ | [`05_batch_cli.py`](examples/05_batch_cli.py) | Drive the batch pipeline from Python |
226
+
227
+ ```bash
228
+ python examples/01_quickstart.py
229
+ python examples/03_real_apollo_mseed.py --data-dir /path/to/mseed --sr 6.625
230
+ ```
231
+
232
+ ## Desktop GUI
233
+
234
+ A PyQt5 application with a two-page main window: a **Repaired DB Viewer**
235
+ (compare the raw and repaired archives) and a **Manual Repair** panel
236
+ (select an interval with the mouse, try repair methods, preview, export).
237
+
238
+ ```bash
239
+ pip install "dssrr[gui]"
240
+ dssrr-gui # console script
241
+ python -m dssrr.gui # equivalent module entry point
242
+ ```
243
+
244
+ On Windows a helper launcher is provided at the repository root
245
+ (`启动DSSRR.bat`); on Unix use `./launch_gui.sh`. Both honour a `DSSRR_PYTHON`
246
+ environment variable if you need a specific interpreter.
247
+
248
+ Both database roots can also be preset from the environment, which is handy for
249
+ scripted launches and editor run configurations:
250
+
251
+ | Variable | Effect |
252
+ |---|---|
253
+ | `DSSRR_RAW_ROOT` | Pre-fills the **Raw DB** field on both the viewer and the Batch Repair page |
254
+ | `DSSRR_FIX_ROOT` | Pre-fills the **Repaired DB** field (otherwise it is derived as `<raw>_repaired`) |
255
+ | `DSSRR_PYTHON` | Interpreter used by `launch_gui.sh` / `启动DSSRR.bat` |
256
+ | `DSSRR_LANG` | Language of the whole interface (and of the exported report). Defaults to `en`; set to `zh` for Chinese. A language picked with the sidebar button overrides it |
257
+
258
+ ```bash
259
+ DSSRR_RAW_ROOT=examples/data DSSRR_FIX_ROOT=examples/data_repaired python -m dssrr.gui
260
+ DSSRR_LANG=zh python -m dssrr.gui # Chinese interface + Chinese report figure
261
+ ```
262
+
263
+ ### Language
264
+
265
+ The GUI is **bilingual (English / 中文)**. Use the `Language: 中文` button at the
266
+ bottom of the sidebar to switch; the choice is remembered in
267
+ `QSettings(APP_ORG, APP_NAME)` under `UI/Language` and restored on the next start.
268
+ Switching re-labels every open panel immediately, including the matplotlib
269
+ navigation toolbars (which are third-party widgets whose text is not routed
270
+ through `self.tr()`).
271
+
272
+ How it works &mdash; there is no `.qm` toolchain involved:
273
+
274
+ * `dssrr/gui/translations_zh.py` is a plain `{english source: chinese}` dict
275
+ (423 entries covering 100% of the `self.tr()` / `QCoreApplication.translate()`
276
+ string constants under `dssrr/gui/`).
277
+ * `dssrr/gui/i18n.py` installs a `QTranslator` **subclass** that looks `source`
278
+ up in that dict and ignores `context`. Because it sits on the `QApplication`,
279
+ all ~580 existing `self.tr()` call sites work untouched, and any new one you
280
+ write is translated for free.
281
+ * A missing entry must return `None` (a null `QString`) so Qt falls back to the
282
+ English source. Returning `""` would be treated as a real, empty translation
283
+ and blank out the whole English interface &mdash; `tests/test_gui_i18n.py`
284
+ locks this down.
285
+
286
+ Text that lives outside the widget tree (report figures, axis labels, the
287
+ severity/recommendation strings in the diagnostics panel, the export-settings
288
+ dialog) is resolved by `dssrr.i18n.resolve_language()`, in this order:
289
+
290
+ 1. an explicit `lang=` argument (e.g. `RepairReportPlotter(sr=..., lang="zh")`),
291
+ 2. the process-level language set by the sidebar button (`dssrr.i18n.set_language`),
292
+ 3. the `DSSRR_LANG` environment variable,
293
+ 4. **English**.
294
+
295
+ The system locale is deliberately *not* consulted &mdash; earlier releases guessed
296
+ from it, which made a Chinese Windows show a Chinese export dialog inside an
297
+ otherwise English interface.
298
+
299
+ ### Segment selection and the zoom/pan mode
300
+
301
+ Dragging on the waveform selects an anomaly segment, but matplotlib's Pan and
302
+ Zoom modes take `canvas.widgetlock`, which silently swallows the selection
303
+ events. When either mode is active the hint next to the navigation toolbar turns
304
+ into a warning &mdash; *"Zoom/pan is active &mdash; segment selection is disabled.
305
+ Click Zoom or Pan again to turn it off, then drag to select."* &mdash; and
306
+ switches back to the normal instructions once you leave the mode.
307
+
308
+ The batch page and the manual panel call **the same algorithms** as the library
309
+ &mdash; there is a single source of truth for the repair maths
310
+ (`tests/test_single_source_of_truth.py` enforces it).
311
+
312
+ ## Batch repair from the command line
313
+
314
+ Cross-platform, no PyQt required &mdash; suitable for headless servers:
315
+
316
+ ```bash
317
+ dssrr-batch \
318
+ --raw-root /data/apollo/raw \
319
+ --fix-root /data/apollo/repair \
320
+ --stations S12,S15 \
321
+ --d0 19760101 --d1 19760131
322
+ ```
323
+
324
+ It runs the full pipeline: E7 anomaly detection &rarr; multi-level repair &rarr;
325
+ moonquake-catalogue protection &rarr; per-month catalog CSV &rarr; automatic
326
+ verification &rarr; summary JSON. Exit status is `0` when verification passes for
327
+ every repaired file.
328
+
329
+ Full option list, directory conventions and worked examples:
330
+ [`docs/CLI.md`](docs/CLI.md).
331
+
332
+ ```python
333
+ from dssrr.cli import quick_scan, batch_repair
334
+
335
+ print(quick_scan("/data/raw", ["S12"], 19760101, 19760131)["files"])
336
+
337
+ summary = batch_repair("/data/raw", "/data/repair", ["S12"],
338
+ 19760101, 19760131)
339
+ print(summary["verify"]["ok"])
340
+ ```
341
+
342
+ ## Parameters
343
+
344
+ `DSSRR(...)` constructor arguments:
345
+
346
+ | Parameter | Default | Meaning |
347
+ |---|---|---|
348
+ | `sr` | &mdash; | Sampling rate in Hz (must be positive) |
349
+ | `reference_sec` | `300.0` | Reference window length **per side**, in seconds |
350
+ | `seed` | `42` | Seed for deterministic synthesis; `None` = fresh randomness |
351
+ | `quantize` | `False` | Round the repaired samples back to integers |
352
+ | `config` | `None` | Overrides merged onto `dssrr.config.DEFAULT_CONFIG` |
353
+
354
+ `repair(...)` / `repair_with_report(...)` keywords: `reference_before_sec`,
355
+ `reference_after_sec`, `safety_gap_sec`, `random_seed`, `quantize`.
356
+
357
+ All thresholds &mdash; detection, spectral estimation, synthesis, layered
358
+ synthesis, joining, verification &mdash; live in one place:
359
+ [`dssrr/config.py`](dssrr/config.py). Pass `config={"verification": {...}}` to
360
+ override any of them.
361
+
362
+ ```python
363
+ from dssrr import get_config
364
+ cfg = get_config()
365
+ cfg["synthesis"]["random_seed"] = 7
366
+ ```
367
+
368
+ ## Key features
369
+
370
+ - **No training required** &mdash; no labelled data, no learned weights, works out
371
+ of the box on any locally stationary seismic time series.
372
+ - **Statistical fidelity** &mdash; preserves PSD, autocorrelation structure and
373
+ energy distribution, not just pointwise smoothness.
374
+ - **Robust for long gaps** &mdash; stable up to 90+ minutes, where simple methods
375
+ collapse.
376
+ - **Seamless boundaries** &mdash; trend alignment and phase-constrained edge
377
+ blending remove visible steps at the joins.
378
+ - **Non-destructive** &mdash; data outside the anomaly interval is never touched.
379
+ - **Lightweight core** &mdash; numpy + scipy only; importing `dssrr` never pulls
380
+ in a GUI toolkit.
381
+ - **Three tiers, one implementation** &mdash; library, CLI and GUI share the same
382
+ code path.
383
+
384
+ ## Limitations
385
+
386
+ - DSSRR reproduces **second-order statistics**, not the original phase inside the
387
+ gap. A transient event during the dropout cannot be reconstructed.
388
+ - It assumes the signal inside the gap is statistically similar to the adjacent
389
+ healthy segments (local stationarity). A regime change across the gap biases
390
+ the fused target spectrum.
391
+ - Reference windows shorter than ~120 s (at 6.625 Hz) give poor low-frequency
392
+ resolution.
393
+ - The bundled `QualityVerifier.spectral_continuity` compares **raw**
394
+ (unsmoothed) periodograms over 0.5&ndash;3 Hz, so its absolute value is
395
+ realisation-dependent: two independent draws from the same process score
396
+ ~0.5 rather than ~1.0. Treat it as a diagnostic, not an absolute
397
+ pass/fail gate &mdash; the batch pipeline's structural acceptance checks
398
+ (`dssrr.repair_lib.e7_repair_build.verify_file`) are the authoritative ones.
399
+
400
+ ## Repository layout
401
+
402
+ ```
403
+ dssrr/
404
+ ├── core.py the DSSRR pipeline (ReferenceSpectrumReplacer)
405
+ ├── spectral.py two-sided PSD estimation and fusion
406
+ ├── synthesizer.py reference-block overlap-add synthesis
407
+ ├── verifier.py quality diagnostics
408
+ ├── config.py every threshold in one dict (DEFAULT_CONFIG)
409
+ ├── cli.py dssrr-batch entry point
410
+ ├── paths.py canonical user file locations (+ legacy fallback)
411
+ ├── gui/ PyQt5 desktop application
412
+ │ ├── main.py integrated main window (viewer + manual repair)
413
+ │ ├── viewer.py raw/repaired archive comparison viewer
414
+ │ ├── manual_repair.py interactive interval repair
415
+ │ ├── depulse_dialog.py the multi-method repair panel
416
+ │ ├── batch_repair.py batch page
417
+ │ └── icons/ app icon (SVG + rasterised PNG/ICO)
418
+ └── repair_lib/ Apollo-specific detection + repair pipeline
419
+ ├── e7_detector.py anomaly detection (MISSING/FREEZE/SAT/SPIKE/…)
420
+ ├── e7_repair_build.py per-file multi-level repair + verification
421
+ ├── repair_engine.py ObsPy glue for the GUI
422
+ ├── depulse_advanced.py Isolation Forest / LOF / NN inpainting
423
+ ├── report_plot.py repair report figures
424
+ └── anomaly_repair/ the three public replacers (+ re-export shims)
425
+ ```
426
+
427
+ ## Sample data, provenance and acknowledgements
428
+
429
+ `examples/data/` contains three 12-hour **Apollo 12 / 15 / 16** long-period
430
+ vertical records (network `XA`, channel `MHZ`, 6.625 Hz, 1976-01-13) so that the
431
+ examples and tests can run against real archival data with no download step.
432
+ They are small, freely redistributable fixtures and are **not** installed with
433
+ the Python package. See [`examples/data/README.md`](examples/data/README.md) for
434
+ the full provenance and licensing statement.
435
+
436
+ The batch pipeline ships a merged moonquake catalogue assembled from:
437
+
438
+ - Gagnepain-Beyneix, J., et al. (2006), *A seismic model of the lunar mantle and
439
+ constraints on Moon's composition*, PEPI.
440
+ - Lognonné, P., et al. (2003), *Moon meteoritic seismic hum: steady state
441
+ prediction*, JGR.
442
+ - The Apollo PSE Long-Period Event Catalog (rev. 1008c).
443
+
444
+ Apollo lunar seismic data were produced by NASA's Apollo Lunar Surface
445
+ Experiments Package (ALSEP) and are distributed openly through the
446
+ [IRIS / EarthScope Data Management Center](https://ds.iris.edu/ds/nodes/dmc/).
447
+ This project is not affiliated with or endorsed by NASA or IRIS.
448
+
449
+ ## FAQ
450
+
451
+ **Does DSSRR need a GPU or training?**
452
+ No. It is a classical spectral method &mdash; numpy + scipy only. The optional
453
+ `[nn]` extra adds a neural inpainting *alternative*, but it is not used by the
454
+ default DSSRR path.
455
+
456
+ **Will it change my data outside the gap?**
457
+ Never. The repaired array is a copy in which only `[start, end]` differs.
458
+
459
+ **Why does my result differ between runs?**
460
+ The synthesis phase is randomised. Pass a fixed `seed` / `random_seed` for
461
+ reproducible output; `None` deliberately requests fresh randomness.
462
+
463
+ **Can I use it on non-Apollo data?**
464
+ Yes &mdash; pass your own `sr` and choose the reference length with
465
+ `auto_reference_length`. The `repair_lib` detection pipeline, however, is tuned
466
+ for Apollo MHZ records and its anomaly classes (MISSING / FREEZE / SAT / SPIKE /
467
+ STEP / BURST) assume that instrument.
468
+
469
+ **A long gap got left unrepaired in batch mode.**
470
+ Gaps longer than `max_repair_sec` are intentionally **not** reconstructed: DSSRR
471
+ marks them in the catalog rather than fabricating data.
472
+
473
+ ## Citation
474
+
475
+ If you use this code, please cite:
476
+
477
+ > Yu, S., Li, X. (2026). Dual-Sided Spectral Reference Reconstruction for
478
+ > long-duration artifact repair in Apollo lunar seismic records.
479
+ > *Seismological Research Letters* (submitted).
480
+
481
+ ## License
482
+
483
+ MIT &mdash; see [LICENSE](LICENSE).