1# -*- coding: utf-8 -*-
2"""
3Defines the libmseed structures and blockettes.
4"""
5from __future__ import (absolute_import, division, print_function,
6                        unicode_literals)
7from future.builtins import *  # NOQA
8from future.utils import native_str
9
10import ctypes as C  # NOQA
11import warnings
12
13import numpy as np
14
15from . import InternalMSEEDError, InternalMSEEDWarning
16from obspy.core.util.libnames import _load_cdll
17
18
19# Load the shared library. Later on it is wrapped into another object that
20# correctly set's up error and warning handles - but information from the
21# library is already required at an earlier point.
22__clibmseed = _load_cdll("mseed")
23
24
25# Size of the off_t type.
26sizeoff_off_t = C.c_int.in_dll(__clibmseed, "LM_SIZEOF_OFF_T").value
27for _c in [C.c_long, C.c_longlong, C.c_int]:
28    if C.sizeof(_c) == sizeoff_off_t:
29        off_t_type = _c
30        break
31else:  # pragma: no cover
32    raise InternalMSEEDError("Could not determine corresponding ctypes "
33                             "datatype for off_t.")
34
35
36HPTERROR = -2145916800000000
37
38ENDIAN = {0: '<', 1: '>'}
39
40
41# XXX: Do we still support Python 2.4 ????
42# Figure out Py_ssize_t (PEP 353).
43#
44# Py_ssize_t is only defined for Python 2.5 and above, so it defaults to
45# ctypes.c_int for earlier versions.
46#
47# https://svn.python.org/projects/ctypes/trunk/
48#           ctypeslib/ctypeslib/contrib/pythonhdr.py
49if hasattr(C.pythonapi, 'Py_InitModule4'):
50    Py_ssize_t = C.c_int
51elif hasattr(C.pythonapi, 'Py_InitModule4_64'):
52    Py_ssize_t = C.c_int64
53else:
54    # XXX: just hard code it for now
55    Py_ssize_t = C.c_int64
56    # raise TypeError("Cannot determine type of Py_ssize_t")
57
58# Valid control headers in ASCII numbers.
59SEED_CONTROL_HEADERS = [ord('V'), ord('A'), ord('S'), ord('T')]
60MINI_SEED_CONTROL_HEADERS = [ord('D'), ord('R'), ord('Q'), ord('M')]
61VALID_CONTROL_HEADERS = SEED_CONTROL_HEADERS + MINI_SEED_CONTROL_HEADERS + \
62    [ord(' ')]
63
64# expected data types for libmseed id: (numpy, ctypes)
65DATATYPES = {b"a": C.c_char, b"i": C.c_int32, b"f": C.c_float,
66             b"d": C.c_double}
67SAMPLESIZES = {'a': 1, 'i': 4, 'f': 4, 'd': 8}
68
69# Valid record lengths for Mini-SEED files.
70VALID_RECORD_LENGTHS = [256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536,
71                        131072, 262144, 524288, 1048576]
72
73# allowed encodings:
74# id: (name, sampletype a/i/f/d, default NumPy type, write support)
75ENCODINGS = {0: ("ASCII", "a", np.dtype(native_str("|S1")).type, True),
76             1: ("INT16", "i", np.dtype(np.int16), True),
77             3: ("INT32", "i", np.dtype(np.int32), True),
78             4: ("FLOAT32", "f", np.dtype(np.float32), True),
79             5: ("FLOAT64", "d", np.dtype(np.float64), True),
80             10: ("STEIM1", "i", np.dtype(np.int32), True),
81             11: ("STEIM2", "i", np.dtype(np.int32), True),
82             12: ("GEOSCOPE24", "f", np.dtype(np.float32), False),
83             13: ("GEOSCOPE16_3", "f", np.dtype(np.float32), False),
84             14: ("GEOSCOPE16_4", "f", np.dtype(np.float32), False),
85             16: ("CDSN", "i", np.dtype(np.int32), False),
86             30: ("SRO", "i", np.dtype(np.int32), False),
87             32: ("DWWSSN", "i", np.dtype(np.int32), False)}
88
89# Encodings not supported by libmseed and consequently ObsPy.
90UNSUPPORTED_ENCODINGS = {
91    2: "INT24",
92    15: "US National Network compression",
93    17: "Graefenberg 16 bit gain ranged",
94    18: "IPG - Strasbourg 16 bit gain ranged",
95    19: "STEIM (3) Comprssion",
96    31: "HGLP Format",
97    33: "RSTN 16 bit gain ranged"
98}
99
100# Maps fixed header activity flags bit number and the matching expected key in
101# the flags_value
102FIXED_HEADER_ACTIVITY_FLAGS = {0: 'calib_signal',
103                               1: 'time_correction',
104                               2: 'begin_event',
105                               3: 'end_event',
106                               4: 'positive_leap',
107                               5: 'negative_leap',
108                               6: 'event_in_progress'}
109
110# Maps fixed header I/O and clock flags bit number and the matching expected
111# key in the flags_value
112FIXED_HEADER_IO_CLOCK_FLAGS = {0: 'sta_vol_parity_error_possible',
113                               1: 'long_record_read',
114                               2: 'short_record_read',
115                               3: 'start_of_time_series',
116                               4: 'end_of_time_series',
117                               5: 'clock_locked'}
118
119# Maps fixed header data quality flags bit number and the matching expected
120# key in the flags_value
121FIXED_HEADER_DATA_QUAL_FLAGS = {0: 'amplifier_sat_detected',
122                                1: 'digitizer_clipping_detected',
123                                2: 'spikes_detected',
124                                3: 'glitches_detected',
125                                4: 'missing_padded_data_present',
126                                5: 'telemetry_sync_error',
127                                6: 'digital_filter_maybe_charging',
128                                7: 'time_tag_questionable'}
129
130# Map the dtype to the samplecode. Redundant information but it is hard coded
131# for performance reasons.
132SAMPLETYPE = {"|S1": "a",
133              "int16": "i",
134              "int32": "i",
135              "float32": "f",
136              "float64": "d",
137              np.dtype(native_str("|S1")).type: "a",
138              np.dtype(np.int16).type: "i",
139              np.dtype(np.int32).type: "i",
140              np.dtype(np.float32).type: "f",
141              np.dtype(np.float64).type: "d"}
142# as defined in libmseed.h
143MS_ENDOFFILE = 1
144MS_NOERROR = 0
145
146
147# SEED binary time
148class BTime(C.Structure):
149    _fields_ = [
150        ('year', C.c_ushort),
151        ('day', C.c_ushort),
152        ('hour', C.c_ubyte),
153        ('min', C.c_ubyte),
154        ('sec', C.c_ubyte),
155        ('unused', C.c_ubyte),
156        ('fract', C.c_ushort),
157    ]
158
159
160# Fixed section data of header
161class FSDHS(C.Structure):
162    _fields_ = [
163        ('sequence_number', C.c_char * 6),
164        ('dataquality', C.c_char),
165        ('reserved', C.c_char),
166        ('station', C.c_char * 5),
167        ('location', C.c_char * 2),
168        ('channel', C.c_char * 3),
169        ('network', C.c_char * 2),
170        ('start_time', BTime),
171        ('numsamples', C.c_ushort),
172        ('samprate_fact', C.c_short),
173        ('samprate_mult', C.c_short),
174        ('act_flags', C.c_ubyte),
175        ('io_flags', C.c_ubyte),
176        ('dq_flags', C.c_ubyte),
177        ('numblockettes', C.c_ubyte),
178        ('time_correct', C.c_int),
179        ('data_offset', C.c_ushort),
180        ('blockette_offset', C.c_ushort),
181    ]
182
183
184# Blockette 100, Sample Rate (without header)
185class Blkt100S(C.Structure):
186    _fields_ = [
187        ('samprate', C.c_float),
188        ('flags', C.c_byte),
189        ('reserved', C.c_ubyte * 3),
190    ]
191blkt_100 = Blkt100S  # noqa
192
193
194# Blockette 200, Generic Event Detection (without header)
195class Blkt200S(C.Structure):
196    _fields_ = [
197        ('amplitude', C.c_float),
198        ('period', C.c_float),
199        ('background_estimate', C.c_float),
200        ('flags', C.c_ubyte),
201        ('reserved', C.c_ubyte),
202        ('time', BTime),
203        ('detector', C.c_char * 24),
204    ]
205
206
207# Blockette 201, Murdock Event Detection (without header)
208class Blkt201S(C.Structure):
209    _fields_ = [
210        ('amplitude', C.c_float),
211        ('period', C.c_float),
212        ('background_estimate', C.c_float),
213        ('flags', C.c_ubyte),
214        ('reserved', C.c_ubyte),
215        ('time', BTime),
216        ('snr_values', C.c_ubyte * 6),
217        ('loopback', C.c_ubyte),
218        ('pick_algorithm', C.c_ubyte),
219        ('detector', C.c_char * 24),
220    ]
221
222
223# Blockette 300, Step Calibration (without header)
224class Blkt300S(C.Structure):
225    _fields_ = [
226        ('time', BTime),
227        ('numcalibrations', C.c_ubyte),
228        ('flags', C.c_ubyte),
229        ('step_duration', C.c_uint),
230        ('interval_duration', C.c_uint),
231        ('amplitude', C.c_float),
232        ('input_channel', C.c_char * 3),
233        ('reserved', C.c_ubyte),
234        ('reference_amplitude', C.c_uint),
235        ('coupling', C.c_char * 12),
236        ('rolloff', C.c_char * 12),
237    ]
238
239
240# Blockette 310, Sine Calibration (without header)
241class Blkt310S(C.Structure):
242    _fields_ = [
243        ('time', BTime),
244        ('reserved1', C.c_ubyte),
245        ('flags', C.c_ubyte),
246        ('duration', C.c_uint),
247        ('period', C.c_float),
248        ('amplitude', C.c_float),
249        ('input_channel', C.c_char * 3),
250        ('reserved2', C.c_ubyte),
251        ('reference_amplitude', C.c_uint),
252        ('coupling', C.c_char * 12),
253        ('rolloff', C.c_char * 12),
254    ]
255
256
257# Blockette 320, Pseudo-random Calibration (without header)
258class Blkt320S(C.Structure):
259    _fields_ = [
260        ('time', BTime),
261        ('reserved1', C.c_ubyte),
262        ('flags', C.c_ubyte),
263        ('duration', C.c_uint),
264        ('ptp_amplitude', C.c_float),
265        ('input_channel', C.c_char * 3),
266        ('reserved2', C.c_ubyte),
267        ('reference_amplitude', C.c_uint),
268        ('coupling', C.c_char * 12),
269        ('rolloff', C.c_char * 12),
270        ('noise_type', C.c_char * 8),
271    ]
272
273
274# Blockette 390, Generic Calibration (without header)
275class Blkt390S(C.Structure):
276    _fields_ = [
277        ('time', BTime),
278        ('reserved1', C.c_ubyte),
279        ('flags', C.c_ubyte),
280        ('duration', C.c_uint),
281        ('amplitude', C.c_float),
282        ('input_channel', C.c_char * 3),
283        ('reserved2', C.c_ubyte),
284    ]
285
286
287# Blockette 395, Calibration Abort (without header)
288class Blkt395S(C.Structure):
289    _fields_ = [
290        ('time', BTime),
291        ('reserved', C.c_ubyte * 2),
292    ]
293
294
295# Blockette 400, Beam (without header)
296class Blkt400S(C.Structure):
297    _fields_ = [
298        ('azimuth', C.c_float),
299        ('slowness', C.c_float),
300        ('configuration', C.c_ushort),
301        ('reserved', C.c_ubyte * 2),
302    ]
303
304
305# Blockette 405, Beam Delay (without header)
306class Blkt405S(C.Structure):
307    _fields_ = [
308        ('delay_values', C.c_ushort * 1),
309    ]
310
311
312# Blockette 500, Timing (without header)
313class Blkt500S(C.Structure):
314    _fields_ = [
315        ('vco_correction', C.c_float),
316        ('time', BTime),
317        ('usec', C.c_byte),
318        ('reception_qual', C.c_ubyte),
319        ('exception_count', C.c_uint),
320        ('exception_type', C.c_char * 16),
321        ('clock_model', C.c_char * 32),
322        ('clock_status', C.c_char * 128),
323    ]
324
325
326# Blockette 1000, Data Only SEED (without header)
327class Blkt1000S(C.Structure):
328    _fields_ = [
329        ('encoding', C.c_ubyte),
330        ('byteorder', C.c_ubyte),
331        ('reclen', C.c_ubyte),
332        ('reserved', C.c_ubyte),
333    ]
334
335
336# Blockette 1001, Data Extension (without header)
337class Blkt1001S(C.Structure):
338    _fields_ = [
339        ('timing_qual', C.c_ubyte),
340        ('usec', C.c_byte),
341        ('reserved', C.c_ubyte),
342        ('framecnt', C.c_ubyte),
343    ]
344blkt_1001 = Blkt1001S  # noqa
345
346
347# Blockette 2000, Opaque Data (without header)
348class Blkt2000S(C.Structure):
349    _fields_ = [
350        ('length', C.c_ushort),
351        ('data_offset', C.c_ushort),
352        ('recnum', C.c_uint),
353        ('byteorder', C.c_ubyte),
354        ('flags', C.c_ubyte),
355        ('numheaders', C.c_ubyte),
356        ('payload', C.c_char * 1),
357    ]
358
359
360# Blockette chain link, generic linkable blockette index
361class BlktLinkS(C.Structure):
362    pass
363
364
365BlktLinkS._fields_ = [
366    ('blktoffset', C.c_ushort),  # Blockette offset
367    ('blkt_type', C.c_ushort),  # Blockette type
368    ('next_blkt', C.c_ushort),  # Offset to next blockette
369    ('blktdata', C.POINTER(None)),  # Blockette data
370    ('blktdatalen', C.c_ushort),  # Length of blockette data in bytes
371    ('next', C.POINTER(BlktLinkS))]
372BlktLink = BlktLinkS  # noqa
373
374
375class StreamstateS(C.Structure):
376    _fields_ = [
377        ('packedrecords', C.c_longlong),  # Count of packed records
378        ('packedsamples', C.c_longlong),  # Count of packed samples
379        ('lastintsample', C.c_int),       # Value of last integer sample packed
380        ('comphistory', C.c_byte),        # Control use of lastintsample for
381                                          # compression history
382    ]
383StreamState = StreamstateS  # noqa
384
385
386class MsrecordS(C.Structure):
387    pass
388
389
390MsrecordS._fields_ = [
391    ('record', C.POINTER(C.c_char)),  # Mini-SEED record
392    ('reclen', C.c_int),              # Length of Mini-SEED record in bytes
393                                      # Pointers to SEED data record structures
394    ('fsdh', C.POINTER(FSDHS)),      # Fixed Section of Data Header
395    ('blkts', C.POINTER(BlktLink)),   # Root of blockette chain
396    ('Blkt100',
397     C.POINTER(Blkt100S)),          # Blockette 100, if present
398    ('Blkt1000',
399     C.POINTER(Blkt1000S)),         # Blockette 1000, if present
400    ('Blkt1001',
401     C.POINTER(Blkt1001S)),         # Blockette 1001, if present
402                                    # Common header fields in accessible form
403    ('sequence_number', C.c_int),     # SEED record sequence number
404    ('network', C.c_char * 11),       # Network designation, NULL terminated
405    ('station', C.c_char * 11),       # Station designation, NULL terminated
406    ('location', C.c_char * 11),      # Location designation, NULL terminated
407    ('channel', C.c_char * 11),       # Channel designation, NULL terminated
408    ('dataquality', C.c_char),        # Data quality indicator
409    ('starttime', C.c_longlong),      # Record start time, corrected (first
410                                      # sample)
411    ('samprate', C.c_double),         # Nominal sample rate (Hz)
412    ('samplecnt', C.c_int64),         # Number of samples in record
413    ('encoding', C.c_byte),           # Data encoding format
414    ('byteorder', C.c_byte),          # Byte order of record
415                                      # Data sample fields
416    ('datasamples', C.c_void_p),      # Data samples, 'numsamples' of type
417                                      # 'sampletype'
418    ('numsamples', C.c_int64),        # Number of data samples in datasamples
419    ('sampletype', C.c_char),         # Sample type code: a, i, f, d
420                                      # Stream oriented state information
421    ('ststate',
422     C.POINTER(StreamState)),         # Stream processing state information
423]
424MSRecord = MsrecordS  # noqa
425
426
427class MstraceS(C.Structure):
428    pass
429
430
431MstraceS._fields_ = [
432    ('network', C.c_char * 11),       # Network designation, NULL terminated
433    ('station', C.c_char * 11),       # Station designation, NULL terminated
434    ('location', C.c_char * 11),      # Location designation, NULL terminated
435    ('channel', C.c_char * 11),       # Channel designation, NULL terminated
436    ('dataquality', C.c_char),        # Data quality indicator
437    ('type', C.c_char),               # MSTrace type code
438    ('starttime', C.c_longlong),      # Time of first sample
439    ('endtime', C.c_longlong),        # Time of last sample
440    ('samprate', C.c_double),         # Nominal sample rate (Hz)
441    ('samplecnt', C.c_int64),         # Number of samples in trace coverage
442    ('datasamples', C.c_void_p),      # Data samples, 'numsamples' of type
443                                      # 'sampletype'
444    ('numsamples', C.c_int64),        # Number of data samples in datasamples
445    ('sampletype', C.c_char),         # Sample type code: a, i, f, d
446    ('prvtptr', C.c_void_p),          # Private pointer for general use
447    ('ststate',
448     C.POINTER(StreamState)),         # Stream processing state information
449    ('next', C.POINTER(MstraceS)),   # Pointer to next trace
450]
451MSTrace = MstraceS  # noqa
452
453
454class MstracegroupS(C.Structure):
455    pass
456
457
458MstracegroupS._fields_ = [
459    ('numtraces', C.c_int),            # Number of MSTraces in the trace chain
460    ('traces', C.POINTER(MstraceS)),  # Root of the trace chain
461]
462MSTraceGroup = MstracegroupS  # noqa
463
464
465# Define the high precision time tick interval as 1/modulus seconds */
466# Default modulus of 1000000 defines tick interval as a microsecond */
467HPTMODULUS = 1000000.0
468
469
470# Reading Mini-SEED records from files
471class MsfileparamS(C.Structure):
472    pass
473
474
475MsfileparamS._fields_ = [
476    ('fp', C.POINTER(Py_ssize_t)),
477    ('filename', C.c_char * 512),
478    ('rawrec', C.c_char_p),
479    ('readlen', C.c_int),
480    ('readoffset', C.c_int),
481    ('packtype', C.c_int),
482    ('packhdroffset', off_t_type),
483    ('filepos', off_t_type),
484    ('filesize', off_t_type),
485    ('recordcount', C.c_int),
486]
487MSFileParam = MsfileparamS  # noqa
488
489
490class UDIFF(C.Union):
491    """
492    Union for Steim objects.
493    """
494    _fields_ = [
495        ("byte", C.c_int8 * 4),  # 4 1-byte differences.
496        ("hw", C.c_int16 * 2),  # 2 halfword differences.
497        ("fw", C.c_int32),  # 1 fullword difference.
498    ]
499
500
501#####################################
502# Define the C structures.
503#####################################
504
505
506# Container for a continuous trace segment, linkable
507class MSTraceSeg(C.Structure):
508    pass
509
510
511MSTraceSeg._fields_ = [
512    ('starttime', C.c_longlong),      # Time of first sample
513    ('endtime', C.c_longlong),        # Time of last sample
514    ('samprate', C.c_double),         # Nominal sample rate (Hz)
515    ('samplecnt', C.c_int64),         # Number of samples in trace coverage
516    ('datasamples', C.c_void_p),      # Data samples, 'numsamples' of type
517                                      # 'sampletype'
518    ('numsamples', C.c_int64),        # Number of data samples in datasamples
519    ('sampletype', C.c_char),         # Sample type code: a, i, f, d
520    ('prvtptr', C.c_void_p),          # Private pointer for general use, unused
521                                      # by libmseed
522    ('prev', C.POINTER(MSTraceSeg)),  # Pointer to previous segment
523    ('next', C.POINTER(MSTraceSeg))   # Pointer to next segment
524]
525
526
527# Container for a trace ID, linkable
528class MSTraceID(C.Structure):
529    pass
530
531
532MSTraceID._fields_ = [
533    ('network', C.c_char * 11),       # Network designation, NULL terminated
534    ('station', C.c_char * 11),       # Station designation, NULL terminated
535    ('location', C.c_char * 11),      # Location designation, NULL terminated
536    ('channel', C.c_char * 11),       # Channel designation, NULL terminated
537    ('dataquality', C.c_char),        # Data quality indicator
538    ('srcname', C.c_char * 45),       # Source name (Net_Sta_Loc_Chan_Qual),
539                                      # NULL terminated
540    ('type', C.c_char),               # Trace type code
541    ('earliest', C.c_longlong),       # Time of earliest sample
542    ('latest', C.c_longlong),         # Time of latest sample
543    ('prvtptr', C.c_void_p),          # Private pointer for general use, unused
544                                      # by libmseed
545    ('numsegments', C.c_int),         # Number of segments for this ID
546    ('first',
547     C.POINTER(MSTraceSeg)),          # Pointer to first of list of segments
548    ('last', C.POINTER(MSTraceSeg)),  # Pointer to last of list of segments
549    ('next', C.POINTER(MSTraceID))    # Pointer to next trace
550]
551
552
553# Container for a continuous trace segment, linkable
554class MSTraceList(C.Structure):
555    pass
556
557
558MSTraceList._fields_ = [
559    ('numtraces', C.c_int),            # Number of traces in list
560    ('traces', C.POINTER(MSTraceID)),  # Pointer to list of traces
561    ('last', C.POINTER(MSTraceID))     # Pointer to last used trace in list
562]
563
564
565# Data selection structure time window definition containers
566class SelectTime(C.Structure):
567    pass
568
569
570SelectTime._fields_ = [
571    ('starttime', C.c_longlong),  # Earliest data for matching channels
572    ('endtime', C.c_longlong),    # Latest data for matching channels
573    ('next', C.POINTER(SelectTime))
574]
575
576
577# Data selection structure definition containers
578class Selections(C.Structure):
579    pass
580
581
582Selections._fields_ = [
583    ('srcname', C.c_char * 100),  # Matching (globbing) source name:
584    # Net_Sta_Loc_Chan_Qual
585    ('timewindows', C.POINTER(SelectTime)),
586    ('next', C.POINTER(Selections))
587]
588
589
590# Container for a continuous linked list of records.
591class ContinuousSegment(C.Structure):
592    pass
593
594
595ContinuousSegment._fields_ = [
596    ('starttime', C.c_longlong),
597    ('endtime', C.c_longlong),
598    ('samprate', C.c_double),
599    ('sampletype', C.c_char),
600    ('hpdelta', C.c_longlong),
601    ('recordcnt', C.c_int64),
602    ('samplecnt', C.c_int64),
603    ('encoding', C.c_byte),
604    ('byteorder', C.c_byte),
605    ('reclen', C.c_int),
606    ('timing_quality', C.c_uint8),
607    ('calibration_type', C.c_int8),
608    ('datasamples', C.c_void_p),  # Data samples, 'numsamples' of type
609    # 'sampletype'
610    ('firstRecord', C.c_void_p),
611    ('lastRecord', C.c_void_p),
612    ('next', C.POINTER(ContinuousSegment)),
613    ('previous', C.POINTER(ContinuousSegment))
614]
615
616
617# A container for continuous segments with the same id
618class LinkedIDList(C.Structure):
619    pass
620
621
622LinkedIDList._fields_ = [
623    ('network', C.c_char * 11),      # Network designation, NULL terminated
624    ('station', C.c_char * 11),      # Station designation, NULL terminated
625    ('location', C.c_char * 11),     # Location designation, NULL terminated
626    ('channel', C.c_char * 11),      # Channel designation, NULL terminated
627    ('dataquality', C.c_char),       # Data quality indicator
628    ('firstSegment',
629     C.POINTER(ContinuousSegment)),  # Pointer to first of list of segments
630    ('lastSegment',
631     C.POINTER(ContinuousSegment)),  # Pointer to last of list of segments
632    ('next',
633     C.POINTER(LinkedIDList)),       # Pointer to next id
634    ('previous',
635     C.POINTER(LinkedIDList)),       # Pointer to previous id
636]
637
638
639##########################################################################
640# Define the argument and return types of all the used libmseed functions.
641##########################################################################
642
643# Declare function of libmseed library, argument parsing
644__clibmseed.mst_init.argtypes = [C.POINTER(MSTrace)]
645__clibmseed.mst_init.restype = C.POINTER(MSTrace)
646
647__clibmseed.mst_free.argtypes = [C.POINTER(C.POINTER(MSTrace))]
648__clibmseed.mst_free.restype = None
649
650__clibmseed.mst_initgroup.argtypes = [C.POINTER(MSTraceGroup)]
651__clibmseed.mst_initgroup.restype = C.POINTER(MSTraceGroup)
652
653__clibmseed.mst_freegroup.argtypes = [C.POINTER(C.POINTER(MSTraceGroup))]
654__clibmseed.mst_freegroup.restype = None
655
656__clibmseed.msr_init.argtypes = [C.POINTER(MSRecord)]
657__clibmseed.msr_init.restype = C.POINTER(MSRecord)
658
659__clibmseed.ms_readmsr_r.argtypes = [
660    C.POINTER(C.POINTER(MSFileParam)), C.POINTER(C.POINTER(MSRecord)),
661    C.c_char_p, C.c_int, C.POINTER(off_t_type), C.POINTER(C.c_int), C.c_short,
662    C.c_short, C.c_short]
663__clibmseed.ms_readmsr_r.restypes = C.c_int
664
665__clibmseed.ms_readtraces.argtypes = [
666    C.POINTER(C.POINTER(MSTraceGroup)), C.c_char_p, C.c_int, C.c_double,
667    C.c_double, C.c_short, C.c_short, C.c_short, C.c_short]
668__clibmseed.ms_readtraces.restype = C.c_int
669
670__clibmseed.ms_readtraces_timewin.argtypes = [
671    C.POINTER(C.POINTER(MSTraceGroup)), C.c_char_p, C.c_int, C.c_double,
672    C.c_double, C.c_int64, C.c_int64, C.c_short, C.c_short, C.c_short,
673    C.c_short]
674__clibmseed.ms_readtraces_timewin.restype = C.c_int
675
676__clibmseed.msr_starttime.argtypes = [C.POINTER(MSRecord)]
677__clibmseed.msr_starttime.restype = C.c_int64
678
679__clibmseed.msr_endtime.argtypes = [C.POINTER(MSRecord)]
680__clibmseed.msr_endtime.restype = C.c_int64
681
682__clibmseed.ms_detect.argtypes = [
683    np.ctypeslib.ndpointer(dtype=np.int8, ndim=1,
684                           flags=native_str('C_CONTIGUOUS')),
685    C.c_int]
686__clibmseed.ms_detect.restype = C.c_int
687
688__clibmseed.msr_decode_steim2.argtypes = [
689    C.c_void_p,
690    C.c_int,
691    C.c_int,
692    np.ctypeslib.ndpointer(dtype=np.int32, ndim=1,
693                           flags=native_str('C_CONTIGUOUS')),
694    C.c_int, C.c_char_p, C.c_int]
695__clibmseed.msr_decode_steim2.restype = C.c_int
696
697__clibmseed.msr_decode_steim1.argtypes = [
698    C.c_void_p,
699    C.c_int,
700    C.c_int,
701    np.ctypeslib.ndpointer(dtype=np.int32, ndim=1,
702                           flags=native_str('C_CONTIGUOUS')),
703    C.c_int, C.c_char_p, C.c_int]
704__clibmseed.msr_decode_steim1.restype = C.c_int
705
706# tricky, C.POINTER(C.c_char) is a pointer to single character fields
707# this is completely different to C.c_char_p which is a string
708__clibmseed.mst_packgroup.argtypes = [
709    C.POINTER(MSTraceGroup), C.CFUNCTYPE(
710        None, C.POINTER(C.c_char), C.c_int, C.c_void_p),
711    C.c_void_p, C.c_int, C.c_short, C.c_short, C.POINTER(C.c_int64), C.c_short,
712    C.c_short, C.POINTER(MSRecord)]
713__clibmseed.mst_packgroup.restype = C.c_int
714
715__clibmseed.msr_addblockette.argtypes = [C.POINTER(MSRecord),
716                                         C.POINTER(C.c_char),
717                                         C.c_int, C.c_int, C.c_int]
718__clibmseed.msr_addblockette.restype = C.POINTER(BlktLink)
719
720__clibmseed.msr_parse.argtypes = [
721    np.ctypeslib.ndpointer(dtype=np.int8, ndim=1), C.c_int,
722    C.POINTER(C.POINTER(MSRecord)),
723    C.c_int, C.c_int, C.c_int]
724__clibmseed.msr_parse.restype = C.c_int
725
726
727# Set the necessary arg- and restypes.
728__clibmseed.readMSEEDBuffer.argtypes = [
729    np.ctypeslib.ndpointer(dtype=np.int8, ndim=1,
730                           flags=native_str('C_CONTIGUOUS')),
731    C.c_int,
732    C.POINTER(Selections),
733    C.c_int8,
734    C.c_int,
735    C.c_int8,
736    C.c_int8,
737    C.c_int,
738    C.CFUNCTYPE(C.c_longlong, C.c_int, C.c_char)
739]
740__clibmseed.readMSEEDBuffer.restype = C.POINTER(LinkedIDList)
741
742
743__clibmseed.setupLogging.argtpyes = [
744    C.c_int8,
745    C.CFUNCTYPE(C.c_void_p, C.c_char_p),
746    C.CFUNCTYPE(C.c_void_p, C.c_char_p)]
747__clibmseed.setupLogging.restype = None
748
749
750__clibmseed.msr_free.argtypes = [C.POINTER(C.POINTER(MSRecord))]
751__clibmseed.msr_free.restype = None
752
753
754__clibmseed.mstl_init.restype = C.POINTER(MSTraceList)
755__clibmseed.mstl_free.argtypes = [C.POINTER(C.POINTER(MSTraceList)), C.c_int]
756
757
758__clibmseed.lil_free.argtypes = [C.POINTER(LinkedIDList)]
759__clibmseed.lil_free.restype = None
760
761
762__clibmseed.allocate_bytes.argtypes = (C.c_int,)
763__clibmseed.allocate_bytes.restype = C.c_void_p
764
765
766__clibmseed.ms_genfactmult.argtypes = [
767    C.c_double,
768    C.POINTER(C.c_int16),
769    C.POINTER(C.c_int16)
770]
771__clibmseed.ms_genfactmult.restype = C.c_int
772
773
774__clibmseed.ms_nomsamprate.argtypes = [
775    C.c_int,
776    C.c_int
777]
778__clibmseed.ms_nomsamprate.restype = C.c_double
779
780
781class _LibmseedWrapper(object):
782    """
783    Wrapper object around libmseed that tries to guarantee that all warnings
784    and errors within libmseed are properly converted to their Python
785    counterparts.
786
787    Might be a bit overengineered but it does the trick and is completely
788    transparent to the user.
789    """
790    def __init__(self, lib):
791        self.lib = lib
792        self.verbose = True
793
794    def __getattr__(self, item):
795        func = getattr(self.lib, item)
796
797        def _wrapper(*args):
798            # Collect exceptions. They cannot be raised in the callback as
799            # they could never be caught then. They are collected and raised
800            # later on.
801            _errs = []
802            _warns = []
803
804            def log_error_or_warning(msg):
805                msg = msg.decode()
806                if msg.startswith("ERROR: "):
807                    msg = msg[7:].strip()
808                    _errs.append(msg)
809                if msg.startswith("INFO: "):
810                    msg = msg[6:].strip()
811                    _warns.append(msg)
812
813            diag_print = \
814                C.CFUNCTYPE(None, C.c_char_p)(log_error_or_warning)
815
816            def log_message(msg):
817                if self.verbose:
818                    print(msg[6:].strip())
819            log_print = C.CFUNCTYPE(None, C.c_char_p)(log_message)
820
821            # Hookup libmseed's logging facilities to it's Python callbacks.
822            self.lib.setupLogging(diag_print, log_print)
823
824            try:
825                return func(*args)
826            finally:
827                for _w in _warns:
828                    warnings.warn(_w, InternalMSEEDWarning)
829                if _errs:
830                    msg = ("Encountered %i error(s) during a call to "
831                           "%s():\n%s" % (
832                               len(_errs), item, "\n".join(_errs)))
833                    raise InternalMSEEDError(msg)
834        return _wrapper
835
836
837clibmseed = _LibmseedWrapper(lib=__clibmseed)
838