# Source code for obspy.io.reftek.util

    # -*- coding: utf-8 -*-
    from __future__ import (absolute_import, division, print_function,
                            unicode_literals)
    from future.builtins import *  # NOQA

    import codecs

    import numpy as np

    from obspy import UTCDateTime
    from obspy.core.compatibility import from_buffer

    def bcd(_i):
        return (_i >> 4 & 0xF).astype(np.uint32) * 10 + (_i & 0xF)

    def bcd_16bit_int(_i):
        _i = bcd(_i)
        return _i[::, 0] * 100 + _i[::, 1]

    def bcd_hex(_i):
        m = _i.shape[1]
        _bcd = codecs.encode(_i.ravel(), "hex_codec").decode("ASCII").upper()
        return from_buffer(_bcd, dtype="|S%d" % (m * 2))

    def bcd_8bit_hex(_i):
        return np.array(["{:X}".format(int(x)) for x in _i], dtype="|S2")

    def bcd_julian_day_string_to_nanoseconds_of_year(_i):
        timestrings = bcd_hex(_i)
        return _timestrings_to_nanoseconds(timestrings)

    _timegm_cache = {}

    def _get_nanoseconds_for_start_of_year(year):
        # Reftek 130 data format stores only the last two digits of the year.
        # We currently assume that 00-49 are years 2000-2049 and 50-99 are years
        # 2050-2099. We deliberately raise an exception in the read routine if the
        # current year will become 2050 (just in case someone really still uses
        # this code then.. ;-)
        if year < 50:
            year += 2000
        else:
            year += 1900
        try:
            ns = _timegm_cache[year]
        except KeyError:
            ns = UTCDateTime(year, 1, 1)._ns
            _timegm_cache[year] = ns
        return ns

    def _timestrings_to_nanoseconds(timestrings):
        """
        Helper routine to convert timestrings of form "DDDHHMMSSsss" to array of
        integer nanosecond POSIX timestamp.

        :param timestring: numpy.ndarray
        :rtype: numpy.ndarray
        """
        # split up the time string into tuple of
        # (day of year, hours, minutes, seconds, milliseconds), still as string
        nanoseconds = [(string[:3], string[3:5], string[5:7],
                        string[7:9], string[9:]) for string in timestrings]
        nanoseconds = np.array(nanoseconds, dtype="S3").astype(np.int64)
        # now scale the columns of the array, so that everything is in nanoseconds
        to_nano = 1000000000
        nanoseconds[:, 0] -= 1
        nanoseconds[:, 0] *= 86400 * to_nano
        nanoseconds[:, 1] *= 3600 * to_nano
        nanoseconds[:, 2] *= 60 * to_nano
        nanoseconds[:, 3] *= to_nano
        nanoseconds[:, 4] *= 1000000
        # sum up days, hours, minutes etc. for every row of the array
        nanoseconds = nanoseconds.sum(axis=1)
        return nanoseconds

    def _decode_ascii(chars):
        return chars.decode("ASCII")

    def _parse_long_time(time_bytestring, decode=True):
        """
        :returns: POSIX timestamp as integer nanoseconds
        """
        if decode:
            time_string = time_bytestring.decode()
        else:
            time_string = time_bytestring
        if not time_string.strip():
            return None
        time_string, milliseconds = time_string[:-3], int(time_string[-3:])
        t = UTCDateTime.strptime(time_string, '%Y%j%H%M%S')
        nanoseconds = t._ns
        nanoseconds += milliseconds * 1000000
        return nanoseconds

    def _16_tuple_ascii(bytestring):
        item_count = 16
        chars = bytestring.decode("ASCII")
        if len(chars) % item_count != 0:
            raise NotImplementedError("Should not happen, contact developers.")
        item_size = int(len(chars) / item_count)
        result = []
        for i in range(item_count):
            chars_ = chars[i * item_size:(i + 1) * item_size]
            result.append(chars_.strip() or None)
        return tuple(result)

    def _16_tuple_int(bytestring):
        ascii_tuple = _16_tuple_ascii(bytestring)
        result = []
        for chars in ascii_tuple:
            if chars is None or not chars.strip():
                result.append(None)
                continue
            result.append(int(chars))
        return tuple(result)

    def _16_tuple_float(bytestring):
        ascii_tuple = _16_tuple_ascii(bytestring)
        result = []
        for chars in ascii_tuple:
            if chars is None or not chars.strip():
                result.append(None)
                continue
            result.append(float(chars))
        return tuple(result)

    if __name__ == '__main__':
        import doctest
        doctest.testmod(exclude_empty=True)

### Thank you!

We would like to thank our contributors, whose efforts make this software what it is. These people have helped by writing code and documentation, and by testing. They have created and maintained this product, its associated libraries and applications, our build tools and our web sites.

#### Contributors

- Ackerley, Nick
- Allgeyer, Sebastien
- Ammon, Charles J.
- Antunes, Emanuel
- Arnarsson, Ólafur St.
- Bank, Markus
- Barsch, Robert
- Behr, Yannik
- Bernardi, Fabrizio
- Bernauer, Felix
- Bes de Berc, Maxime
- Beyreuther, Moritz
- Boltz, Shawn
- Bonaimé, Sébastien
- Carothers, Lloyd
- Chamberlain, Calum
- Chambers, Derrick
- Chen, Zhao
- Clark, Adam
- Danecek, Peter
- Egdorf, Sven
- Engels, Fabian
- Ermert, Laura
- Eulenfeld, Tom

- Fabbri, Tommaso
- Falco, Nicholas
- Fee, Jeremy
- Grellier, Clément
- Grunberg, Marc
- Hammer, Conny
- Heimann, Sebastian
- Heiniger, Lukas
- Hope, Gaute
- Igel, Heiner
- Inza, Adolfo
- Isken, Marius
- Ketchum, David
- Koymans, Mathijs
- Kremers, Simon
- Kress, Victor
- Krieger, Lars
- Krischer, Lion
- Kula, Damian
- Käufl, Paul
- Köhler, Andreas
- Lecocq, Thomas
- Leeman, John
- Legovini, Paride

- Lesage, Philippe
- Li, Yulin
- Lomax, Anthony
- Lopes, Rui L.
- MacCarthy, Jonathan
- Maggi, Alessia
- Martin, Henri
- Medlin, Andrew
- Megies, Tobias
- Meschede, Matthias
- Michelini, Alberto
- Miller, Nathaniel C.
- Morgenstern, Bernhard
- Nof, Ran Novitsky
- Panning, Mark P.
- Parker, Tom
- Pestourie, Romain
- Rapagnani, Giovanni
- Reyes, Celso
- Ringler, Adam
- Rothenhäusler, Nicolas
- Russo, Emiliano
- Sales de Andrade, Elliott

- Satriano, Claudio
- Saul, Joachim
- Scheingraber, Chris
- Schneider, Simon
- Sippl, Christian
- Snoke, Arthur
- Stange, Stefan
- Stähler, Simon C.
- Sullivan, Benjamin
- Tian, Dongdong
- Trabant, Chad
- Trani, Luca
- Uieda, Leonardo
- Walker, Andrew
- Walther, Marcus
- Wassermann, Joachim
- Williams, Mark C.
- Winkelman, Andrew
- Zaccarelli, Riccardo
- Zad, Seyed Kasra Hosseini
- Zhu, Lijun
- van Driel, Martin
- van Wijk, Kasper

#### Funds

ObsPy was partially funded by the

- German Science Foundation (DFG) via grant DFG IG 16/9-1
- German Ministry for Education and Research (BMBF), GEOTECHNOLOGIEN grant 03G0646H.
- NERA project (Network of European Research Infrastructures for Earthquake Risk Assessment and Mitigation) under the European Community's Seventh Framework Programme (FP7/2007-2013) grant agreement n° 262330
- Leibniz Institute for Applied Geophysics (LIAG)
- VERCE EU-FP7 project (no. 283543)