Repository: obspy/obspy

Issue:
PPSD.add¶
Hello guys,

obspy.signal.spectral_estimation.PPSD.add was not working with a data sample I was trying to input.
Then I found info in a forum saying the sample must have at least one hour of continuous record. Please add this info to the documentation. 

Cheers.
PPSD.add¶
Hello guys,

obspy.signal.spectral_estimation.PPSD.add was not working with a data sample I was trying to input.
Then I found info in a forum saying the sample must have at least one hour of continuous record. Please add this info to the documentation. 

Cheers.


Discussion:
The `ppsd_length` argument of `PPSD.__init__()` allows you to steer the length of data passed to the actual psd. I guess we could raise a more appropriate warning somewhere?

@atilapaes Please feel free to submit a PR with an updated documentation or a better warning message that would have helped you diagnose the problem quicker.
Sorry to jump in on such an old issue, but I ran into this problem in my own code.
After a bit of tracking it down, the `PPSD.add` function will fail silently because there is no failure catch in the while loop on line 881 of signal.spectral_estimation, specifically:
 
`
while t1 + self.ppsd_length - tr.stats.delta <= t2:
`

If the while statement never evaluates True then no warning is raised but the information is not added to the ppsd - a silent failure.

One option for a fix could be to add:
```
if t1 + self.ppsd_length - tr.stats.delta > t2:
        msg = ("Trace was shorter than ppsd length. Skipping:\n" +
                   str(tr))
        warnings.warn(msg)
        continue
```
just before the while statement. 
This if statement will only trigger if the initial trace is shorter than the expected window, and then we skip the trace to avoid running the while loop and effectively evaluating the same condition twice.
The `ppsd_length` argument of `PPSD.__init__()` allows you to steer the length of data passed to the actual psd. I guess we could raise a more appropriate warning somewhere?

@atilapaes Please feel free to submit a PR with an updated documentation or a better warning message that would have helped you diagnose the problem quicker.
Sorry to jump in on such an old issue, but I ran into this problem in my own code.
After a bit of tracking it down, the `PPSD.add` function will fail silently because there is no failure catch in the while loop on line 881 of signal.spectral_estimation, specifically:
 
`
while t1 + self.ppsd_length - tr.stats.delta <= t2:
`

If the while statement never evaluates True then no warning is raised but the information is not added to the ppsd - a silent failure.

One option for a fix could be to add:
```
if t1 + self.ppsd_length - tr.stats.delta > t2:
        msg = ("Trace was shorter than ppsd length. Skipping:\n" +
                   str(tr))
        warnings.warn(msg)
        continue
```
just before the while statement. 
This if statement will only trigger if the initial trace is shorter than the expected window, and then we skip the trace to avoid running the while loop and effectively evaluating the same condition twice.

Resolving patch:
diff --git a/obspy/signal/spectral_estimation.py b/obspy/signal/spectral_estimation.py
--- a/obspy/signal/spectral_estimation.py
+++ b/obspy/signal/spectral_estimation.py
@@ -878,6 +878,12 @@ def add(self, stream, verbose=False):
                 continue
             t1 = tr.stats.starttime
             t2 = tr.stats.endtime
+            if t1 + self.ppsd_length - tr.stats.delta > t2:
+                msg = (f"Trace is shorter than this PPSD's 'ppsd_length' "
+                       f"({str(self.ppsd_length)} seconds). Skipping trace: "
+                       f"{str(tr)}")
+                warnings.warn(msg)
+                continue
             while t1 + self.ppsd_length - tr.stats.delta <= t2:
                 if self.__check_time_present(t1):
                     msg = "Already covered time spans detected (e.g. %s), " + \


Test patch:
diff --git a/obspy/signal/tests/test_spectral_estimation.py b/obspy/signal/tests/test_spectral_estimation.py
--- a/obspy/signal/tests/test_spectral_estimation.py
+++ b/obspy/signal/tests/test_spectral_estimation.py
@@ -16,7 +16,7 @@
 from obspy import Stream, Trace, UTCDateTime, read, read_inventory, Inventory
 from obspy.core import Stats
 from obspy.core.inventory import Response
-from obspy.core.util import AttribDict
+from obspy.core.util import AttribDict, CatchAndAssertWarnings
 from obspy.core.util.base import NamedTemporaryFile
 from obspy.core.util.obspy_types import ObsPyException
 from obspy.io.xseed import Parser
@@ -888,3 +888,21 @@ def test_can_add_npz_without_pickle(self):
             # If not allow_pickle,  a helpful error msg should be raised.
             with pytest.raises(ValueError, match='Loading PPSD results'):
                 ppsd.add_npz(temp_path)
+
+    def test_short_trace_warning(self):
+        """
+        Makes sure a warning is shown if a trace shorter than ppsd length is
+        added which is doing nothing. see #2386
+        """
+        tr, paz = _get_sample_data()
+        ppsd = PPSD(stats=tr.stats, metadata=paz)
+        tr.data = tr.data[:4]  # shorten trace
+        msg = (f"Trace is shorter than this PPSD's 'ppsd_length' "
+               f"({str(ppsd.ppsd_length)} seconds). Skipping trace: "
+               f"{str(tr)}")
+        with CatchAndAssertWarnings(expected=[(UserWarning, msg)]):
+            success = ppsd.add(tr)
+        assert not success  # add returns False on noop
+        assert not len(ppsd.times_processed)  # should be empty, nothing added
+        # contains start/end times of traces added in, even if not processed
+        assert len(ppsd.times_data) == 1