Repository: obspy/obspy

Issue:
Cannot write zero sample rate trace data as miniSEED
**Description of bug**
ObsPy cannot write zero sample rate miniSEED. This is important for non time series data, such as LOG channels.

**To reproduce the bug**
1.) Download some zero sample rate LOG data from the DMC.

e.g. http://service.iris.edu/fdsnws/dataselect/1/query?network=YW&channel=LOG&starttime=2016-01-01&endtime=2016-12-31&nodata=404

2.) Read this 0 sample rate data using ObsPy

```python
import obspy
st = obspy.read("fdsnws-dataselect_2019-10-23t18_11_00z.mseed")
```

3.) Try to write this data as miniSEED

```python
st.write('fdsnws-dataselect_2019-10-23t18_11_00z.mseed', format='MSEED')
```

resulting in the following exception

```
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "//anaconda2/lib/python2.7/site-packages/obspy/core/stream.py", line 1443, in write
    write_format(self, filename, **kwargs)
  File "//anaconda2/lib/python2.7/site-packages/obspy/io/mseed/core.py", line 626, in _write_mseed
    (1.0 / trace.stats.sampling_rate * HPTMODULUS) % 100 != 0:
ZeroDivisionError: float division by zero
```

**System info**
  -  ObsPy version: 1.1.1
  - Python version: 2.7 
  - Platform: OSX
  - ObsPy was installed using Anaconda


Discussion:
This is easy to fix, I just tested it, only this if/else needs changing.
With this change the writing works fine it seems:

```python
 641         if starttime % 100 != 0 or \
 642            (trace.stats.sampling_rate and (1.0 / trace.stats.sampling_rate * HPTMODULUS) % 100 != 0):
 643             use_blkt_1001 = True
```


But I'm not aware how this blockette 1001 stuff should be treated with 0 sps data, so I'll leave it to @krischer 
ping @krischer 

Resolving patch:
diff --git a/obspy/io/mseed/core.py b/obspy/io/mseed/core.py
--- a/obspy/io/mseed/core.py
+++ b/obspy/io/mseed/core.py
@@ -637,8 +637,9 @@ def _write_mseed(stream, filename, encoding=None, reclen=None, byteorder=None,
         # of more than 100 microseconds, or if timing quality is set, \
         # Blockette 1001 will be written for every record.
         starttime = util._convert_datetime_to_mstime(trace.stats.starttime)
-        if starttime % 100 != 0 or \
-           (1.0 / trace.stats.sampling_rate * HPTMODULUS) % 100 != 0:
+        if starttime % 100 != 0 or (
+                trace.stats.sampling_rate and
+                (1.0 / trace.stats.sampling_rate * HPTMODULUS) % 100 != 0):
             use_blkt_1001 = True
 
         if hasattr(trace.stats, 'mseed') and \


Test patch:
diff --git a/obspy/io/mseed/tests/test_mseed_reading_and_writing.py b/obspy/io/mseed/tests/test_mseed_reading_and_writing.py
--- a/obspy/io/mseed/tests/test_mseed_reading_and_writing.py
+++ b/obspy/io/mseed/tests/test_mseed_reading_and_writing.py
@@ -1595,6 +1595,27 @@ def test_read_mseed_sr0(self):
         self.assertEqual(''.join(tr.data.astype(str)),
                          '001:00:00:00 REF TEK 130\r\n')
 
+    def test_reading_and_writing_zero_sampling_rate_traces(self):
+        """
+        LOG channels for example usually have sampling rates of zero.
+        """
+        tr = Trace(
+            data=np.linspace(0, 1, 10),
+            header={
+                "sampling_rate": 0.0,
+                "network": "AA",
+                "station": "CC",
+                "channel": "LOG"})
+        with io.BytesIO() as buf:
+            tr.write(buf, format="mseed")
+            buf.seek(0, 0)
+            tr2 = read(buf)[0]
+
+        # Delete meta-data that gets added during reading.
+        del tr2.stats["_format"]
+        del tr2.stats["mseed"]
+        self.assertEqual(tr, tr2)
+
 
 def suite():
     return unittest.makeSuite(MSEEDReadingAndWritingTestCase, 'test')