Repository: obspy/obspy

Issue:
Trace.stats.mseed number_of_records showing info of full file.
Hi all, is there a reason that for all traces loaded from an mSEED file that every Trace.stats.mseed dictionary shows info for the entire file, and not the single trace itself?

```
for tr in st:
    print tr.stats.mseed
```

Will always give the following result

```
{
    'record_length': 512,
    'encoding': 'STEIM2', 
    'filesize': 3684864, 
    'dataquality': 'R', 
    'number_of_records': 7197, 
    'byteorder': '>'
}
```

IMHO it make more sense to change the number of records to the number that is actually contained within the trace and not the entire file. Thoughts?



Discussion:

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
@@ -299,12 +299,10 @@ def _read_mseed(mseed_object, starttime=None, endtime=None, headonly=False,
             info['encoding']
         raise ValueError(msg)
 
+    record_length = info["record_length"]
+
     # Only keep information relevant for the whole file.
-    info = {'encoding': info['encoding'],
-            'filesize': info['filesize'],
-            'record_length': info['record_length'],
-            'byteorder': info['byteorder'],
-            'number_of_records': info['number_of_records']}
+    info = {'filesize': info['filesize']}
 
     # If it's a file name just read it.
     if isinstance(mseed_object, (str, native_str)):
@@ -313,9 +311,6 @@ def _read_mseed(mseed_object, starttime=None, endtime=None, headonly=False,
     elif hasattr(mseed_object, 'read'):
         bfr_np = np.fromstring(mseed_object.read(), dtype=np.int8)
 
-    # Get the record length
-    record_length = info["record_length"]
-
     # Search for data records and pass only the data part to the underlying C
     # routine.
     offset = 0
@@ -449,6 +444,12 @@ def allocate_data(samplecount, sampletype):
             header['sampling_rate'] = current_segment.samprate
             header['starttime'] = \
                 util._convert_mstime_to_datetime(current_segment.starttime)
+            header['mseed']['number_of_records'] = current_segment.recordcnt
+            header['mseed']['encoding'] = \
+                ENCODINGS[current_segment.encoding][0]
+            header['mseed']['byteorder'] = \
+                "<" if current_segment.byteorder == 0 else ">"
+            header['mseed']['record_length'] = current_segment.reclen
             if details:
                 timing_quality = current_segment.timing_quality
                 if timing_quality == 0xFF:  # 0xFF is mask for not known timing
@@ -474,7 +475,7 @@ def allocate_data(samplecount, sampletype):
             header = dict((k, v.decode()) if isinstance(v, bytes) else (k, v)
                           for k, v in header.items())
             trace = Trace(header=header, data=data)
-            # Append information.
+            # Append global information.
             for key, value in info.items():
                 setattr(trace.stats.mseed, key, value)
             traces.append(trace)
diff --git a/obspy/io/mseed/headers.py b/obspy/io/mseed/headers.py
--- a/obspy/io/mseed/headers.py
+++ b/obspy/io/mseed/headers.py
@@ -598,7 +598,11 @@ class ContinuousSegment(C.Structure):
     ('samprate', C.c_double),
     ('sampletype', C.c_char),
     ('hpdelta', C.c_longlong),
+    ('recordcnt', C.c_int64),
     ('samplecnt', C.c_int64),
+    ('encoding', C.c_byte),
+    ('byteorder', C.c_byte),
+    ('reclen', C.c_int),
     ('timing_quality', C.c_uint8),
     ('calibration_type', C.c_int8),
     ('datasamples', C.c_void_p),  # Data samples, 'numsamples' of type


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
@@ -1499,6 +1499,53 @@ def assert_valid(filename, reference, test_type):
         # Make sure 23 files have been tested.
         self.assertEqual(count, 24)
 
+    def test_per_trace_mseed_attributes(self):
+        """
+        Tests that most mseed specific attributes like record count, record
+        length and so on are set per trace and not globally.
+        """
+        # Create a concatenated tests file.
+        data_files = ["test.mseed", "two_channels.mseed",
+                      "BW.BGLD.__.EHE.D.2008.001.first_10_records"]
+        data_files = [os.path.join(self.path, "data", _i) for _i in data_files]
+
+        with io.BytesIO() as buf:
+            for d in data_files:
+                with io.open(d, "rb") as fh:
+                    buf.write(fh.read())
+            buf.seek(0, 0)
+            st = read(buf)
+
+        self.assertEqual(len(st), 4)
+        self.assertEqual(st[0].stats.mseed, {
+            'byteorder': '>',
+            'dataquality': 'R',
+            'encoding': 'STEIM2',
+            'filesize': 14336,
+            'number_of_records': 2,
+            'record_length': 4096})
+        self.assertEqual(st[1].stats.mseed, {
+            'byteorder': '>',
+            'dataquality': 'D',
+            'encoding': 'STEIM2',
+            'filesize': 14336,
+            'number_of_records': 1,
+            'record_length': 512})
+        self.assertEqual(st[2].stats.mseed, {
+            'byteorder': '>',
+            'dataquality': 'D',
+            'encoding': 'STEIM2',
+            'filesize': 14336,
+            'number_of_records': 1,
+            'record_length': 512})
+        self.assertEqual(st[3].stats.mseed, {
+            'byteorder': '>',
+            'dataquality': 'D',
+            'encoding': 'STEIM1',
+            'filesize': 14336,
+            'number_of_records': 10,
+            'record_length': 512})
+
 
 def suite():
     return unittest.makeSuite(MSEEDReadingAndWritingTestCase, 'test')