Skip to content
Snippets Groups Projects
LibTrace.py 60.8 KiB
Newer Older
#!/usr/bin/env python

#
# LibTrace
#
# classes for Segy & Mseed
#   identify file types
#   read mseed headers
#
# author: bcb

# Notes 2004.101

# SEED
# build a writer

# SEGY
# SEGY needs to be expanded
# build a writer

##########################
# 2004.141
# Modification
# Author: bcb
#
# MSEED
#   created UbytetoStr to decompose Ubyte to bit fields
#   modified blks 200, 201, 300, 310, 320, 390 and fixhdr to utilize
# 2004.295
# Modification
# Author: bcb
#
# corrected bug in mseed.calcrate

##########################
# 2005.026
# Modification
# Author: bcb
#
# added blockette 2000 to Mseed class
# Blockettes with BTime time definitions now pass the BTime
#    as a tuple within the blk list
# changed unpack formats to use native byte order with standard size &
# alignment

##########################
# 2005.035
# Modification
# Author: bcb
#
# added check for endianess in Mseed & Segy classes, self.ByteOrder
# changed all unpack formats to reflect above change in Mseed & Segy classes
# NOTE: for mseed it is possible to have mixed endianess (e.g. little-endian
#       headers and big-endian data). At this time, this library does not
#       handle data and makes the determination of endianess based soley on
#       the header.

##########################
# 2005.138
# Modification
# Author: bcb
#
# added
# Mseed.Pad, Mseed.FlagStrtoInt, Mseed.WriteFixedHdr and all of
# Mseed.Writeblk???
# fixed improper casting of Time Correction in Mseed.fixedhdr to signed long
##########################
# 2006.179
# bug fix
# Author: bcb
#
# text for little endian in class Segy:ByteOrder had an error such that
# little endian files would never be found
##########################
# 2006.271
# bug fix
# Author: bcb
#
# index error in Mseed:tracelength that caused trace length calculation
# to be 1 sample too long.
##########################
# 2006.335
# bug fix
# Author: bcb
#
# changed Mseed.__init__ to populate identification header fields
# fixed oversight in handling flags in blk100 reads (forgot to
#    implement UbyteToStr
# fixed improper handling of res fields in blk100, blk201 and blk400
##########################
# 2007.342
# cleanup
# Author: bcb
#
# Re-organized WriteFixedHdr to pack all packets prior to any writes. Avoids
#    partially written fixed header
##########################
# 2008.038
#
# Author: bcb
#
# added GetBlk, PutBlk
##########################
# 2008.179
#
# Author: bcb
#
# minimized file reads
# introduced FixedHeader class
# maintained old functionality for now.
##########################
# 2008.204
#
# Author: bcb
#
# corrected typo from DHQaul to DHQual
# added local variables to class Mseed
##########################
# modification
# version: 2020.211
# author: Maeva Pourpoint
#
# Fixed issue with exec and variable scope (see GetBlk() and PutBlk())
##########################

import os
import string
import struct
import sys
import time

# VERSION = "2008.204"

    """
    file utilities class
    """

    def __init__(self, infile):
        self.infile = open(infile, 'rb')
    def close(self):
        self.infile.close()
    def where(self):
        return self.infile.tell()

#########################################################
    # first 20char
    textblk = timeblk = sampblk = miscblk = []
    textblk = []
    Serial = DHQual = Res = Stat = Chan = Loc = Net = None
    timeblk = []
    Year = Day = Hour = Min = Sec = Micro = None
    sampblk = []
    NumSamp = SampFact = SampMult = None
    miscblk = []
    act = io = DataDHQualFL = numblock = timcorr = bdata = bbblock = None

#########################################################
    def __init__(self, infile):
        """
        initialize file, determine byteorder, sizes, time, and load first fixed
        header
        self.type = self.rate = None
            futils.__init__(self, infile)
            # local variable to class
            self.infileseek = self.infile.seek
            self.infilewrite = self.infile.write
            self.infileread = self.infile.read
            self.sunpack = struct.unpack
            self.spack = struct.pack

            self.byteorder = self.ByteOrder()
            if self.byteorder != "unknown":
                # otherwise it might be mseed
                self.type = "mseed"
                # read 1st fixed header in file
                hdrs = self.fixedhdr()
                (self.filesize, self.blksize) = self.sizes()
                (self.numblocks, odd_size) = divmod(
                    self.filesize, self.blksize)
                if odd_size:
                    pass
                # starttime of trace
                # self.time = string.join(list(map(str, (self.FH.Year,
                #                                        self.FH.Day,
                #                                        self.FH.Hour,
                #                                        self.FH.Min,
                #                                        self.FH.Sec))), ":")
                templist = list(map(str, (self.FH.Year, self.FH.Day,
                                          self.FH.Hour, self.FH.Min,
                                          self.FH.Sec)))
                self.time = ":".join(templist)

#########################################################
        determines if processed file is mseed (return 1) or unknown type
        (return 0)
        # if we don't know byteorder it must not be mseed
        if self.byteorder == "unknown":
            return 0
        else:
            return 1

#########################################################
    def idhdr(self):
        """
        trace id info
        """
        return (self.type, self.FH.Stat, self.FH.Chan, self.FH.Loc,
                self.FH.Net, self.time, self.rate)

#########################################################
    def FirstLastTime(self):
        """
        returns first and last block times in epoch
        """
        try:
            # get first fixed header and calculate beginning epoch
            btime_str = time.strptime(self.time, '%Y:%j:%H:%M:%S')
            bepoch = time.mktime(btime_str) + (self.FH.Micro * 0.0001)
            # figure out where the end of the file is and read the last fixed
            # header and calculate eepoch
            (numblocks, odd_size) = divmod(self.filesize, self.blksize)
            self.infileseek(-self.blksize, 2)
            loc = self.infile.tell()
            etime = self.btime2time(loc)
            etime_str = time.strptime(etime, '%Y:%j:%H:%M:%S')
            eepoch = time.mktime(etime_str) + (self.FH.Micro * 0.0001)
            return bepoch, eepoch
        except Exception as e:
            return e
#########################################################
    def tracelength(self):
        """
        returns tracelength in seconds
        """
        try:
            # get first fixed header and calculate beginning epoch
            (bepoch, eepoch) = self.FirstLastTime()
            # here I have to get the last few samples and calculate how much
            # time is accounted for
            self.infileseek(-self.blksize + 30, 2)
            sampblock = self.infileread(2)
            fmtstr = self.fmt_order + "H"
            numsamp = self.sunpack(fmtstr, sampblock)[0]
            lastsamples = (numsamp - 1) / self.rate
            return ((eepoch + lastsamples) - bepoch)
        except Exception:
#########################################################

    def btime2time(self, seekval=0):
        """
        reads file fixed header and returns time string from btime
        """
        try:
            hdrs = self.fixedhdr(seekval)
            # time = string.join(list(map(str, (self.FH.Year, self.FH.Day,
            #                                   self.FH.Hour,self.FH.Min,
            #                                   self.FH.Sec))), ":")
            templist = list(map(str, (self.FH.Year, self.FH.Day, self.FH.Hour,
                                      self.FH.Min, self.FH.Sec)))
            time = ":".join(templist)
            return time
#########################################################

    def idread(self, seekval=0):
        """
        read file as if it is mseed just pulling necessary info
        from fixed header
        """
        try:
            hdrs = self.fixedhdr(seekval)
            return (self.FH.Stat, self.FH.Chan, self.FH.Loc, self.FH.Net,
                    self.rate, self.FH.Year, self.FH.Day, self.FH.Hour,
                    self.FH.Min, self.FH.Sec, self.FH.Micro)
        except Exception:
#########################################################
    def calcrate(self):
        """
        this routine assumes that idread has been called first

        calculate the sample rate of mseed data
        If Sample rate factor > 0 and Sample rate Multiplier > 0,
            rate = Sampfact X Sampmult
        If Sample rate factor > 0 and Sample rate Multiplier < 0,
            rate = -1 X Sampfact/Sampmult
        If Sample rate factor < 0 and Sample rate Multiplier > 0,
            rate = -1 X Sampmult/Sampfact
        If Sample rate factor < 0 and Sample rate Multiplier < 0,
            rate = 1/(Sampfact X Sampmult)
        """
        sampFact = float(self.FH.SampFact)
        sampMult = float(self.FH.SampMult)
        if sampFact > 0 and sampMult > 0:
            rate = sampFact * sampMult
        elif sampFact > 0 and sampMult < 0:
            rate = -1.0 * (sampFact / sampMult)
        elif sampFact < 0 and sampMult > 0:
            rate = -1.0 * (sampMult / sampFact)
        elif sampFact < 0 and sampMult < 0:
            rate = 1.0 / (sampFact * sampMult)
        else:
            rate = sampFact
        return rate
#########################################################

    def ByteOrder(self, seekval=20):
        """
        read file as if it is mseed just pulling time info
        from fixed header and determine if it makes sense unpacked
        as big endian or little endian
        """
        Order = "unknown"
        try:
            # seek to timeblock and read
            self.infileseek(seekval)
            timeblock = self.infileread(10)
            # timeblock=self.TraceBuffer[seekval:seekval+10]
            # assume big endian
            (Year, Day, Hour, Min, Sec, junk, Micro) =\
                self.sunpack('>HHBBBBH', timeblock)
            # test if big endian read makes sense
            if 1950 <= Year <= 2050 and \
               1 <= Day <= 366 and \
               0 <= Hour <= 23 and \
               0 <= Min <= 59 and \
               0 <= Sec <= 59:
                Order = "big"
                self.fmt_order = ">"
            else:
                # try little endian read
                (Year, Day, Hour, Min, Sec, junk, Micro) =\
                    self.sunpack('<HHBBBBH', timeblock)
                # test if little endian read makes sense
                if 1950 <= Year <= 2050 and \
                   1 <= Day <= 366 and \
                   0 <= Hour <= 23 and \
                   0 <= Min <= 59 and \
                   0 <= Sec <= 59:
                    Order = "little"
                    self.fmt_order = "<"
#########################################################
    def sizes(self, seekval=0):
        """
        Finds Blockette 1000 and returns file size & Data Record Length
        """
        try:
            # determine file size
            self.infileseek(0, 2)
            filesize = self.infile.tell()
            # proceed to seekval
            self.infileseek(seekval)
            # self.infileseek(39)
            # assign number of blockettes and offset to next blockette
            nblock = self.FH.miscblk[3]
            nextblock = self.FH.miscblk[6]
            n = 0
            # find blockette 1000
            while n < nblock:
                self.infileseek(nextblock)
                (blktype, newblock) = self.typenxt(nextblock)
                if not blktype:
                    return None, None
                if blktype == 1000:
                    (type, next, encode, order, length,
                     res) = self.blk1000(nextblock)
                    return filesize, 2**length
                nextblock = newblock
                n += 1
        except Exception:
            return None, None

#########################################################
    def typenxt(self, seekval=0):
        """
        Reads first 4 bytes of blockette
        Returns blockette type and next blockette offset
        """
        try:
            self.infileseek(seekval)
            fmtstr = self.fmt_order + "HH"
            (type, next) = self.sunpack(fmtstr, self.infileread(4))
            # reset back to beginning of blockette
            self.infileseek(-4, 1)
            return type, next
            return None, None

#########################################################
    def setEndian(self, endianess=""):
        """
        set format string for endian type
        """
        if endianess == "big":
        elif endianess == "little":
            fmtorderstr = self.fmt_order
        return fmtorderstr

#########################################################
#
# for blockette descriptions below
# from SEED manual
#
# Field     nbits   Description
# UBYTE     8       Unsigned quantity
# BYTE      8       Two's complement signed quantity
# UWORD     16      Unsigned quantity
# WORD      16      Two's complement signed quantity
# ULONG     32      Unsigned quantity
# LONG      32      Two's complement signed quantity
# CHAR*n        n*8     n characters, each 8 bit and each with
#                       a 7-bit ASCII character (high bit always 0)
# FLOAT     32      IEEE Floating point number
#   UWORD   16  Year (e.g. 1987)
#   UWORD   16  J-Day
#   UBYTE   8   Hours of day (0-23)
#   UBYTE   8   Minutes of hour (0-59)
#   UBYTE   8   Seconds of minute (0-59, 60 for leap seconds)
#   UBYTE   8   Unused for data (required for alignment)(
#   UWORD   16  .0001 seconds (0-9999)
#########################################################
    def fixedhdr(self, seekval=0):
        """
        Reads fixed header of 48 bytes (see SEED manual)
        Returns four tuples
            textblk (SeqNum, DHQual, res, Stat, Loc, Chan, Net)
                Sequence Number (CHAR*6)
                Data header/quality indicator (CHAR*1)
                Reserved (CHAR*1)
                Station identifier code (CHAR*5)
                Location identifier (CHAR*2)
                Channel identifier (CHAR*3)
                Network Code (CHAR*2)
            timeblk (Year, Day, Hour, Min, Sec, junk, Micro)
                Year (UWORD, 2)
                Jday (UWORD, 2)
                Hour (UBYTE, 1)
                Min (UBYTE, 1)
                Sec (UBYTE, 1)
                unused (UBYTE, 1)
                .0001 seconds (0-9999) (UWORD, 2)
            sampblk (NumSamp, SampFact, SampMult)
                Number of samples (UWORD, 2)
                Sample rate factor (see calcrate) (WORD, 2)
                Sample rate multiplier (see calcrate) (WORD, 2)
            miscblk (act, io, DataDHQualFl, numblock, timcorr, bdata, bblock)
                Activity flags (UBYTE, 1)
                I/O and clock flags (UBYTE, 1)
                Data quality flags (UBYTE, 1)
                Number of blockettes that follow (UBYTE, 1)
                Time correction (LONG, 4)
                Offset to beginning of data (UWORD, 2)
                Offset to beginning of next blockette (UWORD, 2)
        """
#       self.sunpack=self.sunpack
        try:
            del self.FH
            pass
        self.FH = FixedHeader()
        try:
            self.infileseek(seekval)
            fhblk = self.infileread(48)
            # station info
            fmtstr = self.fmt_order + "6scc5s2s3s2s"
            self.FH.textblk = self.sunpack(fmtstr, fhblk[0:20])
            fmtstr = self.fmt_order + "HHBBBBH"
            self.FH.timeblk = self.sunpack(fmtstr, fhblk[20:30])
            fmtstr = self.fmt_order + "Hhh"
            self.FH.sampblk = self.sunpack(fmtstr, fhblk[30:36])
            fmtstr = self.fmt_order + "BBBBlHH"
            tmpblk = self.sunpack(fmtstr, fhblk[36:48])
            # decompose tmpblk[0-2] into bit fields, create miscblk
            for i in range(3):
                self.FH.miscblk = self.UbytetoStr(tmpblk, i)
                tmpblk = self.FH.miscblk  # recast tmpblk as list

            (self.FH.Serial, self.FH.DHQual, res, self.FH.Stat, self.FH.Loc, self.FH.Chan, self.FH.Net)\
                = self.FH.textblk  # noqa: E501
            (self.FH.Year, self.FH.Day, self.FH.Hour, self.FH.Min, self.FH.Sec, junk, self.FH.Micro)\
                = self.FH.timeblk  # noqa: E501
            (self.FH.NumSamp, self.FH.SampFact, self.FH.SampMult)\
                = self.FH.sampblk
            self.rate = self.calcrate()
            return (self.FH.textblk, self.FH.timeblk, self.FH.sampblk,
                    self.FH.miscblk)
            # return textblk,timeblk,sampblk,miscblk
        except Exception:
            print("error reading fixed header")
            pass

#########################################################
    def WriteFixedHdr(self, hdrlist, seekval=0, endianess=""):
        """
        Writes fixed header of 48 bytes (see SEED manual)
        Requires four tuples (see self.fixedhdr)
        """

        try:
            self.infileseek(seekval)
            fmtorderstr = self.setEndian(endianess)
            # station info
            # left justify values
            # fmtstr0 = fmtorderstr + "6scc5s2s3s2s"

            SeqNum = self.Pad(hdrlist[0][0], 6)  # SeqNum
            DQual = hdrlist[0][1]  # DataQual
            Res = hdrlist[0][2]  # Reserved
            Stat = self.Pad(hdrlist[0][3], 5)  # Stat
            Loc = self.Pad(hdrlist[0][4], 2)  # Loc
            Chan = self.Pad(hdrlist[0][5], 3)  # Chan
            Net = self.Pad(hdrlist[0][6], 2)  # Net

            # time info
            # fmtstr1 = fmtorderstr + "HHBBBBH"
            (Year, Day, Hour, Min, Sec, junk, Micro) = hdrlist[1]
            Micro = int(Micro)

            # sample info
            # fmtstr2 = fmtorderstr + "Hhh"
            (NumSamp, SampFact, SampMult) = hdrlist[2]

            # misc info
            # fmtstr3 = fmtorderstr + "BBBBlHH"
            (actFlags, ioFlags, dqFlags, numblk, timecor,
             offsetData, offsetBlktt) = hdrlist[3]
            # convert flag string to integer
            actFlags = self.FlagStrtoInt(actFlags)
            ioFlags = self.FlagStrtoInt(ioFlags)
            dqFlags = self.FlagStrtoInt(dqFlags)

            # pack fields
            fmtstr = fmtorderstr + "6scc5s2s3s2sHHBBBBHHhhBBBBlHH"
            pack_hdr = self.spack(fmtstr, SeqNum, DQual, Res, Stat, Loc, Chan,
                                  Net, Year, Day, Hour, Min, Sec, junk, Micro,
                                  NumSamp, SampFact, SampMult, actFlags,
                                  ioFlags, dqFlags, numblk, timecor,
                                  offsetData, offsetBlktt)
            self.infilewrite(pack_hdr)

            return 48
            print("error writing fixed header")
            return 0

#########################################################
    def GetBlk(self, blknum, seekval=0):
        ValidBlk = (100, 200, 201, 300, 310, 320, 390,
                    395, 400, 405, 500, 1000, 1001, 2000)
        if blknum not in ValidBlk:
            blk = locals()
            callcmd = "blk=self.blk" + str(blknum) + "(" + str(seekval) + ")"
            exec(callcmd, {}, blk)
#########################################################
    def PutBlk(self, blknum, indata, endian, seekval=0):
        """
        test for valid blockette, if true write blockette
        """
        ValidBlk = (100, 200, 201, 300, 310, 320, 390,
                    395, 400, 405, 500, 1000, 1001, 2000)
        if blknum not in ValidBlk:
            bytes_written = locals()
            callcmd = "bytes_written=self.Writeblk" + \
                str(blknum) + "(indata" + "," + str(seekval) + ", endian)"
            exec(callcmd, {}, bytes_written)
            return bytes_written
#########################################################
    def blk100(self, seekval=0):
        """
        Sample Rate Blockette 100 (12 bytes)
        Returns tuple
                blk
        Blockette type (UWORD, 2)
        Next blockette's byte offset relative to fixed section of header
                (UWORD, 2)
        Actual sample rate (FLOAT, 4)
        Flags (BYTE, 1)
        Reserved (UBYTE, 3)
        """
        self.infileseek(seekval)
        fmtstr = self.fmt_order + "HHfb3B"
        blk = self.sunpack(fmtstr, self.infileread(12))
        scrblk = self.UbytetoStr(blk, 3)

#########################################################
    def Writeblk100(self, inblk, seekval=0, endianess=""):
        """
        writes Sample Rate Blockette 100 (12 bytes)
        requires tuple inblk=(blkette, nextblk, actrate, flags, res0, res1,
        res2)
        """

        try:
            self.infileseek(seekval)
            fmtorderstr = self.setEndian(endianess)
            fmtstr = fmtorderstr + "HHfb3B"
            (blkette, nextblk, actrate, flags, res0, res1, res2) = inblk
            flags = self.FlagStrtoInt(flags)
            pack_blk = self.spack(fmtstr, blkette, nextblk,
                                  actrate, flags, res0, res1, res2)
            self.infilewrite(pack_blk)
            return 12
            print("error writing blockette 100")
            return 0

#########################################################
    def blk200(self, seekval=0):
        """
        Generic Event Detection Blockette 200 (52 bytes)
        Returns tuple
                blk
        Blockette type (UWORD, 2)
        Next blockette's byte offset relative to fixed section of header
                (UWORD, 2)
        Signal amplitude (FLOAT, 4)
        Signal period (FLOAT,4)
        Background estimate (FLOAT,4)
        Event detection flags (UBYTE, 1)
        Reserved (UBYTE, 1)
        Signal onset time (BTime expanded, 10)
        Detector Name (CHAR*24)
        """
        self.infileseek(seekval)
        fmtstr = self.fmt_order + "HHfffBB"
        blk1 = self.sunpack(fmtstr, self.infileread(18))
        fmtstr = self.fmt_order + "HHBBBBH"
        timeblk = self.sunpack(fmtstr, self.infileread(10))
        fmtstr = self.fmt_order + "24s"
        blk2 = self.sunpack(fmtstr, self.infileread(24))

        # incorporate timeblk tuple into blk list
        tmpblk = list(blk1)
        tmpblk.append(timeblk)
        tmpblk = tmpblk + list(blk2)

        # apply bit mask to Ubyte
        blk = self.UbytetoStr(tmpblk, 5)
        return blk

#########################################################
    def Writeblk200(self, inblk, seekval=0, endianess=""):
        """
        writes Generic Event Detection Blockette 200 (52 bytes)
        requires tuple inblk
        inblk=(blkette, nextblk, amp, period, bg, flags, Year, Day, Hour, Min,
        Sec, junk, Micro, charstr)
        """

        try:
            self.infileseek(seekval)
            fmtorderstr = self.setEndian(endianess)
            fmtstr = fmtorderstr + "HHfffBBHHBBBBH24s"

            (blkette, nextblk, amp, period, bg, flags,
             Year, Day, Hour, Min, Sec, junk, Micro, charstr) = inblk
            flags = FlagsStrtoInt(flags)
            pack_blk = self.spack(fmtstr, blkette, nextblk, amp, period, bg,
                                  flags, Year, Day, Hour, Min, Sec, junk,
                                  Micro, charstr)
            self.infilewrite(pack_blk)
            return 52
            print("error writing blockette 200")
            return 0

#########################################################
    def blk201(self, seekval=0):
        """
        Murdock Event Detection Blockette 201 (60 bytes)
        Returns tuple
                blk
        Blockette type (UWORD, 2)
        Next blockette's byte offset relative to fixed section of header
                (UWORD, 2)
        Signal amplitude (FLOAT, 4)
        Signal period (FLOAT,4)
        Background estimate (FLOAT,4)
        Event detection flags (UBYTE, 1)
        Reserved (UBYTE, 1)
        Signal onset time (BTime expanded, 10)
        Signal-to-noise ration values (UBYTE*6)
        Lookback value (UBYTE, 1)
        Pick algorithym (UBYTE, 1)
        Detector Name (CHAR*24)
        """
        self.infileseek(seekval)
        fmtstr = self.fmt_order + "HHfffBB"
        blk1 = self.sunpack(fmtstr, self.infileread(18))
        fmtstr = self.fmt_order + "HHBBBBH"
        timeblk = self.sunpack(fmtstr, self.infileread(10))
        fmtstr = self.fmt_order + "6BBB24s"
        blk2 = self.sunpack(fmtstr, self.infileread(32))

        # incorporate timeblk tuple into blk list
        tmpblk = list(blk1)
        tmpblk.append(timeblk)
        tmpblk = tmpblk + list(blk2)

        # apply bit mask to Ubyte
        blk = self.UbytetoStr(tmpblk, 5)
        return blk

#########################################################
    def Writeblk201(self, inblk, seekval=0, endianess=""):
        """
        writes Murdock Event Detection Blockette 201 (60 bytes)
        requires tuple inblk
        inblk=(blkette, nextblk, amp, period, bg, flags, res, Year, Day, Hour,
               Min, Sec, junk, Micro, SN0, SN1, SN2, SN3, SN4, SN5, loop, algo,
               Name)
        """

        try:
            self.infileseek(seekval)
            fmtorderstr = self.setEndian(endianess)
            fmtstr = fmtorderstr + "HHfffBBHHBBBBH6BBB24s"

            (blkette, nextblk, amp, period, bg, flags, res, Year, Day, Hour,
             Min, Sec, junk, Micro, SN0, SN1, SN2, SN3, SN4, SN5, loop, algo,
             Name) = inblk
            flags = self.FlagStrtoInt(flags)
            pack_blk = self.spack(fmtstr, blkette, nextblk, amp, period, bg,
                                  flags, res, Year, Day, Hour, Min, Sec, junk,
                                  Micro, SN0, SN1, SN2, SN3, SN4, SN5, loop,
                                  algo, Name)
            self.infilewrite(pack_blk)
            return 60
            print("error writing blockette 201")
            return 0

#########################################################
    def blk300(self, seekval=0):
        """
        Step Calibration Blockette 300 (60 bytes)
        Returns tuple
                blk
        Blockette type (UWORD, 2)
        Next blockette's byte offset relative to fixed section of header
                (UWORD, 2)
        Beginning of calibration time (BTime expanded, 10)
        Number of step calibrations (UBYTE, 1)
        Calibrations flags (UBYTE, 1)
        Step duration (ULONG, 4)
        Interval durations (ULONG, 4)
        Calibration signal amplitude (FLOAT, 4)
        Channel with calibration input (CHAR*3)
        Reserved (UBYTE, 1)
        Reference amplitude (ULONG, 4)
        Coupling (CHAR*12)
        Rolloff (CHAR*12)
        """
        self.infileseek(seekval)
        fmtstr = self.fmt_order + "HH"
        blk1 = self.sunpack(fmtstr, self.infileread(4))
        fmtstr = self.fmt_order + "HHBBBBH"
        timeblk = self.sunpack(fmtstr, self.infileread(10))
        fmtstr = self.fmt_order + "BBIIf3sBI12s12s"
        blk2 = self.sunpack(fmtstr, self.infileread(46))

        # incorporate timeblk tuple into blk list
        tmpblk = list(blk1)
        tmpblk.append(timeblk)
        tmpblk = tmpblk + list(blk2)

        # apply bit mask to Ubyte
        blk = self.UbytetoStr(tmpblk, 4)
        return blk

#########################################################
    def Writeblk300(self, inblk, seekval=0, endianess=""):
        """
        writes Step Calibration Blockette 300 (60 bytes)
        requires tuples inblk
        inblk=(blkette, nextblk, Year, Day, Hour, Min, Sec, junk, Micro,
            numst, flags, dur, interv, amp, chan, res, ref, couple, rolloff)
        """

        try:
            self.infileseek(seekval)
            fmtorderstr = self.setEndian(endianess)
            fmtstr = fmtorderstr + "HHHHBBBBHBBIIf3sBI12s12s"

            (blkette, nextblk, Year, Day, Hour, Min, Sec, junk, Micro, numst,
             flags, dur, interv, amp, chan, res, ref, couple,
             rolloff) = inblk
            flags = self.FlagStrtoInt(flags)
            pack_blk = self.spack(fmtstr, blkette, nextblk, Year, Day, Hour,
                                  Min, Sec, junk, Micro, numst, flags, dur,
                                  interv, amp, chan, res, ref, couple, rolloff)
            self.infilewrite(pack_blk)
            return 60
            print("error writing blockette 300")
            return 0

#########################################################
    def blk310(self, seekval=0):
        """
        Sine Calibration Blockette 310 (60 bytes)
        Returns tuple
                blk
        Blockette type (UWORD, 2)
        Next blockette's byte offset relative to fixed section of header
                (UWORD, 2)
        Beginning of calibration time (BTime expanded, 10)
        Reserved (UBYTE, 1)
        Calibrations flags (UBYTE, 1)
        Calibration duration (ULONG, 4)
        Period of signal (FLOAT, 4)
        Amplitude of signal (FLOAT, 4)
        Channel with calibration input (CHAR*3)
        Reserved (UBYTE, 1)
        Reference amplitued (ULONG, 4)
        Coupling (CHAR*12)
        Rolloff (CHAR*12)
        """
        self.infileseek(seekval)
        fmtstr = self.fmt_order + "HH"
        blk1 = self.sunpack(fmtstr, self.infileread(4))
        fmtstr = self.fmt_order + "HHBBBBH"
        timeblk = self.sunpack(fmtstr, self.infileread(10))
        fmtstr = self.fmt_order + "BBIff3sBI12s12s"
        blk2 = self.sunpack(fmtstr, self.infileread(46))

        # incorporate timeblk tuple into blk list
        tmpblk = list(blk1)
        tmpblk.append(timeblk)
        tmpblk = tmpblk + list(blk2)

        # apply bit mask to Ubyte
        blk = self.UbytetoStr(tmpblk, 4)
        return blk

#########################################################
    def Writeblk310(self, inblk, seekval=0, endianess=""):
        """
        writes Sine Calibration Blockette 310 (60 bytes)
        requires tuples inblk
        inblk=(blkette, nextblk, Year, Day, Hour, Min, Sec, junk, Micro, res,
               flags, dura,per,ampsig,chan, res2,refamp,coup,rolloff)
        """

        try:
            self.infileseek(seekval)
            fmtorderstr = self.setEndian(endianess)
            fmtstr = fmtorderstr + "HHHHBBBBHBBIff3sBI12s12s"

            (blkette, nextblk, Year, Day, Hour, Min, Sec, junk, Micro,
             res, flags, dura, per, ampsig, chan, res2, refamp, coup,
             rolloff) = inblk
            flags = self.FlagStrtoInt(flags)
            pack_blk = self.spack(fmtstr, blkette, nextblk, Year, Day, Hour,
                                  Min, Sec, junk, Micro, res, flags, dura, per,
                                  ampsig, chan, res2, refamp, coup, rolloff)
            self.infilewrite(pack_blk)
            return 60
            print("error writing blockette 310")
            return 0

#########################################################
    def blk320(self, seekval=0):
        """
        Pseudo-random Calibraton Blockette 320 (64 bytes)
        Returns tuple
                blk
        Blockette type (UWORD, 2)
        Next blockette's byte offset relative to fixed section of header
                (UWORD, 2)
        Beginning of calibration time (BTime expanded, 10)
        Reserved (UBYTE, 1)
        Calibrations flags (UBYTE, 1)
        Calibration duration (ULONG, 4)
        Peak-to-peak amplitude of steps (FLOAT, 4)
        Channel with calibration input (CHAR*3)
        Reserved (UBYTE, 1)
        Reference amplitued (ULONG, 4)
        Coupling (CHAR*12)
        Rolloff (CHAR*12)
        Noise type (CHAR*8)
        """
        self.infileseek(seekval)
        fmtstr = self.fmt_order + "HH"
        blk1 = self.sunpack(fmtstr, self.infileread(4))
        fmtstr = self.fmt_order + "HHBBBBH"
        timeblk = self.sunpack(fmtstr, self.infileread(10))
        fmtstr = self.fmt_order + "BBIf3sBI12s12s8s"
        blk2 = self.sunpack(fmtstr, self.infileread(50))

        # incorporate timeblk tuple into blk list
        tmpblk = list(blk1)
        tmpblk.append(timeblk)
        tmpblk = tmpblk + list(blk2)

        # apply bit mask to Ubyte
        blk = self.UbytetoStr(tmpblk, 4)
        return blk

#########################################################
    def Writeblk320(self, inblk, seekval=0, endianess=""):
        """
        writes Pseudo-random Calibraton Blockette 320 (64 bytes)
        requires tuples inblk
        inblk=(blkette, nextblk, Year, Day, Hour, Min, Sec, junk, Micro,
            res, flags, dura,ptop,chan, res2,refamp,coup,rolloff,noise)
        """

        try:
            self.infileseek(seekval)