Repository: obspy/obspy

Issue:
trigger_onset() fails if characteristic function does not drop below off-threshold before hitting the end of the characteristic function
ObsPy: master branch, installed as `pip install -e .`
Ubuntu 20.04 LTS

Should `thr_on` be always less than `thr_off`, make a change to the `trigger_onset` function and throw an error respectively.

From the tutorials:

```python
cft = delayed_sta_lta(trace.data, int(5 * df), int(10 * df))
plot_trigger(trace, cft, 5, 10)  # thr_on < thr_off
...
cft = classic_sta_lta(trace.data, int(5. * df), int(10. * df))
plot_trigger(trace, cft, 1.5, 0.5)  # thr_on > thr_off
```

Now I want to swap the thresholds:

```python
cft = classic_sta_lta(trace.data, int(5. * df), int(10. * df))
plot_trigger(trace, cft, 0.5, 1.5)  # thr_on < thr_off

Traceback (most recent call last):
  File "/home/dizcza/PycharmProjects/other/obspy/misc/docs/source/tutorial/code_snippets/trigger_tutorial_classic_sta_lta.py", line 9, in <module>
    plot_trigger(trace, cft, 0.5, 1.5)
  File "/home/dizcza/PycharmProjects/other/obspy/obspy/signal/trigger.py", line 556, in plot_trigger
    on_off = np.array(trigger_onset(cft, thr_on, thr_off))
  File "/home/dizcza/PycharmProjects/other/obspy/obspy/signal/trigger.py", line 378, in trigger_onset
    while of[0] < on[0]:
IndexError: deque index out of range
```


trigger_onset() fails if characteristic function does not drop below off-threshold before hitting the end of the characteristic function
ObsPy: master branch, installed as `pip install -e .`
Ubuntu 20.04 LTS

Should `thr_on` be always less than `thr_off`, make a change to the `trigger_onset` function and throw an error respectively.

From the tutorials:

```python
cft = delayed_sta_lta(trace.data, int(5 * df), int(10 * df))
plot_trigger(trace, cft, 5, 10)  # thr_on < thr_off
...
cft = classic_sta_lta(trace.data, int(5. * df), int(10. * df))
plot_trigger(trace, cft, 1.5, 0.5)  # thr_on > thr_off
```

Now I want to swap the thresholds:

```python
cft = classic_sta_lta(trace.data, int(5. * df), int(10. * df))
plot_trigger(trace, cft, 0.5, 1.5)  # thr_on < thr_off

Traceback (most recent call last):
  File "/home/dizcza/PycharmProjects/other/obspy/misc/docs/source/tutorial/code_snippets/trigger_tutorial_classic_sta_lta.py", line 9, in <module>
    plot_trigger(trace, cft, 0.5, 1.5)
  File "/home/dizcza/PycharmProjects/other/obspy/obspy/signal/trigger.py", line 556, in plot_trigger
    on_off = np.array(trigger_onset(cft, thr_on, thr_off))
  File "/home/dizcza/PycharmProjects/other/obspy/obspy/signal/trigger.py", line 378, in trigger_onset
    while of[0] < on[0]:
IndexError: deque index out of range
```




Discussion:
@dizcza reminder to check if this is still relevant after your PRs ?
@ThomasLecocq yes, it's still relevant - I've not fixed this because the code is convoluted and I got lost.
Can confirm that this is an actual bug. It happens when the event trigger is active towards the end of the characteristic function and then the ctf never falls below the off-threshold before hitting the end of the ctf.

![Figure_2](https://user-images.githubusercontent.com/1842780/157486097-8fd72991-f3c6-4213-8ace-43ea33b07b1a.png)

so what happens is your second event trigger never goes above your off threshold and therefore also cant fall below it before trace ends..


Glad that you worked it out.
I was not using the function. I just opened the issue to let you know.
This issue has been mentioned on **ObsPy Forum**. There might be relevant details there:

https://discourse.obspy.org/t/problem-in-trigger-onset-function/1413/2

@dizcza reminder to check if this is still relevant after your PRs ?
@ThomasLecocq yes, it's still relevant - I've not fixed this because the code is convoluted and I got lost.
Can confirm that this is an actual bug. It happens when the event trigger is active towards the end of the characteristic function and then the ctf never falls below the off-threshold before hitting the end of the ctf.

![Figure_2](https://user-images.githubusercontent.com/1842780/157486097-8fd72991-f3c6-4213-8ace-43ea33b07b1a.png)

so what happens is your second event trigger never goes above your off threshold and therefore also cant fall below it before trace ends..


Glad that you worked it out.
I was not using the function. I just opened the issue to let you know.
This issue has been mentioned on **ObsPy Forum**. There might be relevant details there:

https://discourse.obspy.org/t/problem-in-trigger-onset-function/1413/2


Resolving patch:
diff --git a/obspy/signal/trigger.py b/obspy/signal/trigger.py
--- a/obspy/signal/trigger.py
+++ b/obspy/signal/trigger.py
@@ -348,6 +348,10 @@ def trigger_onset(charfct, thres1, thres2, max_len=9e99, max_len_delete=False):
     else:
         # include it
         of.extend([ind2[-1]])
+
+    # add last sample to ensure trigger gets switched off if ctf does not fall
+    # below off-threshold before hitting the end
+    of.append(len(charfct))
     #
     pick = []
     while on[-1] > of[0]:


Test patch:
diff --git a/obspy/signal/tests/test_trigger.py b/obspy/signal/tests/test_trigger.py
--- a/obspy/signal/tests/test_trigger.py
+++ b/obspy/signal/tests/test_trigger.py
@@ -213,6 +213,22 @@ def test_trigger_onset(self):
             plt.legend()
             plt.show()
 
+    def test_trigger_onset_issue_2891(self):
+        """
+        Regression test for issue 2891
+
+        This used to raise an error if a trigger was activated near the end of
+        the trace, and all sample values after that trigger on threshold are
+        above the designated off threshold. So basically this can only happen
+        if the on threshold is below the off threshold, which is kind of
+        unusual, but we fixed it nevertheless, since people can run into this
+        playing around with different threshold settings
+        """
+        tr = read(os.path.join(
+            self.path, 'BW.UH1._.EHZ.D.2010.147.a.slist.gz'))[0]
+        cft = recursive_sta_lta(tr.data, 5, 30)
+        trigger_onset(cft, 2.5, 3.2)
+
     def test_coincidence_trigger(self):
         """
         Test network coincidence trigger.