diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000000000000000000000000000000000000..6524a3fd7cb85e8093c49e99c381fdbc7c0230c9 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,36 @@ +image: python + +# Change pip's cache directory to be inside the project directory since we can +# only cache local items. +variables: + PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache" + +# Pip's cache doesn't store the python packages +# https://pip.pypa.io/en/stable/reference/pip_install/#caching +# +# If you want to also cache the installed packages, you have to install +# them in a virtualenv and cache it as well. +cache: + paths: + - .cache/pip + +stages: +- test + +before_script: +- pip install -e .[dev] + + +python2: + image: python:2.7 + tags: + - passoft + stage: test + script: tox -e py27 + +python3: + image: python:3.6 + tags: + - passoft + stage: test + script: tox -e py36 diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 2c685e63b491669adf6c524d2381769b5a12e92f..2bd1c65d78181c1413cb027982434a3d85689043 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -32,7 +32,7 @@ wanted" is open to whoever wants to implement it. Implement Features ~~~~~~~~~~~~~~~~~~ -Look through the GitHub issues for features. Anything tagged with "enhancement" +Look through the GitLab issues for features. Anything tagged with "enhancement" and "help wanted" is open to whoever wants to implement it. Write Documentation @@ -65,7 +65,7 @@ Ready to contribute? Here's how to set up `fixhdr` for local development. 3. Install your local copy:: - $ python setup.py develop + $ pip install -e .[dev] 4. Create a branch for local development:: @@ -88,10 +88,10 @@ Ready to contribute? Here's how to set up `fixhdr` for local development. Pull Request Guidelines ----------------------- -Before you submit a pull request, check that it meets these guidelines: +Before you submit a merge request, check that it meets these guidelines: -1. The pull request should include tests. -2. If the pull request adds functionality, the docs should be updated. Put +1. The merge request should include tests. +2. If the merge request adds functionality, the docs should be updated. Put your new functionality into a function with a docstring, and add the feature to the list in README.rst. 3. The pull request should work for Python 2.7 diff --git a/HISTORY.rst b/HISTORY.rst index 53188249591583cad74358f06e7ad957e1da2e6b..fb69cdf9e027842ee58d78873a8afc50e0af5b73 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -2,7 +2,7 @@ History ======= -2009.175 (2018-05-22) +2009.175 (2018-06-07) ------------------ * First release on new build system. diff --git a/LICENSE b/LICENSE index 00981d2036665d22ebcd05fa656518e5870cbe9e..2524e40cc3facbb6f4284d7cbdcad544d4e5435a 100644 --- a/LICENSE +++ b/LICENSE @@ -1,7 +1,7 @@ GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 - Modifies mseed headers. + Modifies mseed headers Copyright (C) 2018 IRIS PASSCAL This program is free software: you can redistribute it and/or modify diff --git a/README.rst b/README.rst index ae5e6279948ac6a3411f703e722c78af34f4104e..f401ff8bfe459721e0d9c71bfce3e1186adddd73 100644 --- a/README.rst +++ b/README.rst @@ -3,7 +3,7 @@ fixhdr ====== -Modifies mseed headers. +Modifies mseed headers * Free software: GNU General Public License v3 (GPLv3) @@ -18,7 +18,7 @@ Features Credits ------- -This package was created with Cookiecutter_ and the `audreyr/cookiecutter-pypackage`_ project template. +This package was created with Cookiecutter_ and the `passoft/cookiecutter`_ project template. .. _Cookiecutter: https://github.com/audreyr/cookiecutter -.. _`audreyr/cookiecutter-pypackage`: https://github.com/audreyr/cookiecutter-pypackage +.. _`passoft/cookiecutter`: https://git.passcal.nmt.edu/passoft/cookiecutter diff --git a/conda.recipe/meta.yaml b/conda.recipe/meta.yaml new file mode 100644 index 0000000000000000000000000000000000000000..2ca8a2c0933613f75ab2d0d508f0fe795125f037 --- /dev/null +++ b/conda.recipe/meta.yaml @@ -0,0 +1,24 @@ +package: + name: fixhdr + version: 2009.175 + +source: + path: .. + +build: + # If the installation is complex, or different between Unix and Windows, use + # separate bld.bat and build.sh files instead of this key. Add the line + # "skip: True # [py<35]" (for example) to limit to Python 3.5 and newer, or + # "skip: True # [not win]" to limit to Windows. + script: python setup.py install --single-version-externally-managed --record=record.txt + +requirements: + build: + - python + - setuptools + run: + - python + +about: + home: https://git.passcal.nmt.edu/passoft/fixhdr + summary: Modifies mseed headers diff --git a/fixhdr/LibTrace.py b/fixhdr/LibTrace.py new file mode 100755 index 0000000000000000000000000000000000000000..0aa5923ae379c056cc8dcd5efc5f3132d755ce4f --- /dev/null +++ b/fixhdr/LibTrace.py @@ -0,0 +1,1651 @@ +#!/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 + +import os, string, sys, struct, exceptions, time +SPACE = " " + +#VERSION = "2008.204" + +class futils: + """ + file utilities class + """ + def __init__(self,infile): + self.infile = open(infile, 'r+b') + + def close(self): + self.infile.close() + + def where(self): + return self.infile.tell() + +######################################################### +class FixedHeader: + """ + mseed fixhdr + """ + #first 20char + textblk=timeblk=sampblk=miscblk=[] + textblk=[] + Serial=DHQual=Res=Stat=Chan=Loc=Net=None + # 20:30 + timeblk=[] + Year=Day=Hour=Min=Sec=Micro=None + # 30:36 + sampblk=[] + NumSamp=SampFact=SampMult=None + # 36:48 + miscblk=[] + act=io=DataDHQualFL=numblock=timcorr=bdata=bbblock=None +######################################################### +class Mseed(futils): + def __init__(self, infile): + """ + initialize file, determine byteorder, sizes, time, and load first fixed header + """ + self.type=self.rate=None + try: + 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(map(str, (self.FH.Year, self.FH.Day, self.FH.Hour, \ + self.FH.Min, self.FH.Sec)), ":") + except: + pass + +######################################################### + def isMseed(self) : + """ + 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 StandardError, 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: + pass +######################################################### + def btime2time(self, seekval=0) : + """ + reads file fixed header and returns time string from btime + """ + try : + hdrs=self.fixedhdr(seekval) + time = string.join(map(str, (self.FH.Year, self.FH.Day, self.FH.Hour, \ + self.FH.Min, self.FH.Sec)), ":") + return time + except: + pass +######################################################### + 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 : + return +######################################################### + 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 = "<" + except : + pass + + return 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: + 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 + except: + return None, None + +######################################################### + def setEndian(self, endianess=""): + """ + set format string for endian type + """ + if endianess == "big": + fmtorderstr=">" + elif endianess == "little": + fmtorderstr="<" + else: + 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 +# +# BTIME +# 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) + """ + #local variable +# self.sunpack=self.sunpack + try: + del self.FH + except: + 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]) + + #time info + fmtstr = self.fmt_order + "HHBBBBH" + self.FH.timeblk=self.sunpack(fmtstr,fhblk[20:30]) + + #sample info + fmtstr = self.fmt_order + "Hhh" + self.FH.sampblk=self.sunpack(fmtstr, fhblk[30:36]) + + #misc info + 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 + (self.FH.Year, self.FH.Day, self.FH.Hour, self.FH.Min, self.FH.Sec, junk, self.FH.Micro)\ + =self.FH.timeblk + (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: + 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) + + #write header + self.infilewrite(pack_hdr) + + return 48 + except: + 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 not blknum in ValidBlk: + return 0 + else: + callcmd="blk=self.blk" + str(blknum) + "(" + str(seekval) + ")" + exec callcmd + return 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 not blknum in ValidBlk: + return 0 + else: + callcmd="bytes_written=self.Writeblk" + str(blknum) + "(indata" + "," + str(seekval) + ", endian)" + exec callcmd + 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) + blk=scrblk + return blk + +######################################################### + 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 + except: + 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 + + except: + 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 + + except: + 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 + + except: + 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 + + except: + 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) + fmtorderstr=self.setEndian(endianess) + fmtstr = fmtorderstr + "HHHHBBBBHBBIf3sBI12s12s8s" + + (blkette, nextblk, Year, Day, Hour, Min, Sec, junk, Micro, \ + res, flags, dura,ptop,chan, res2,refamp,coup,rolloff,noise)=inblk + flags=self.FlagStrtoInt(flags) + pack_blk=self.spack(fmtstr, blkette, nextblk, Year, Day, Hour, Min, Sec, junk, Micro, \ + res, flags, dura,ptop,chan, res2,refamp,coup,rolloff,noise) + self.infilewrite(pack_blk) + return 64 + + except: + print "error writing blockette 320" + return 0 + +######################################################### + def blk390(self, seekval=0): + """ + Generic Calibraton Blockette 390 (28 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) + Calibration signal amplitude (FLOAT, 4) + Channel with calibration input (CHAR*3) + Reserved (UBYTE, 1) + """ + 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 + "BBIf3sB" + blk2=self.sunpack(fmtstr, self.infileread(14)) + + # 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 Writeblk390(self, inblk, seekval=0, endianess=""): + """ + writes Generic Calibraton Blockette 390 (28 bytes) + requires tuples inblk + inblk=(blkette, nextblk, Year, Day, Hour, Min, Sec, junk, Micro, \ + res, flags, dura,amp,chan, res2) + """ + + try: + self.infileseek(seekval) + fmtorderstr=self.setEndian(endianess) + fmtstr = fmtorderstr + "HHHHBBBBHBBIf3sB" + + (blkette, nextblk, Year, Day, Hour, Min, Sec, junk, Micro, \ + res, flags, dura,amp,chan, res2)=inblk + flags=self.FlagStrtoInt(flags) + pack_blk=self.spack(fmtstr, blkette, nextblk, Year, Day, Hour, Min, Sec, junk, Micro, \ + res, flags, dura,amp,chan, res2) + self.infilewrite(pack_blk) + return 28 + + except: + print "error writing blockette 390" + return 0 + +######################################################### + def blk395(self, seekval=0): + """ + Calibraton Abort Blockette 395 (16 bytes) + Returns tuple + blk + Blockette type (UWORD, 2) + Next blockette's byte offset relative to fixed section of header + (UWORD, 2) + End of calibration time (BTime expanded, 10) + Reserved (UBYTE, 2) + """ + self.infileseek(seekval) + fmtstr = self.fmt_order + "HHHHBBBBH2s" + blk=self.sunpack(fmtstr, self.infileread(16)) + return list(blk) + +######################################################### + def Writeblk395(self, inblk, seekval=0, endianess=""): + """ + writes Calibraton Abort Blockette 395 (16 bytes) + requires tuples inblk + inblk=(blkette, nextblk, Year, Day, Hour, Min, Sec, junk, Micro, res) + """ + + try: + self.infileseek(seekval) + fmtorderstr=self.setEndian(endianess) + fmtstr = fmtorderstr + "HHHHBBBBH2s" + + (blkette, nextblk, Year, Day, Hour, Min, Sec, junk, Micro, res)=inblk + pack_blk=self.spack(fmtstr, blkette, nextblk, Year, Day, Hour, Min, Sec, junk, Micro, res) + self.infilewrite(pack_blk) + return 16 + + except: + print "error writing blockette 395" + return 0 + +######################################################### + def blk400(self, seekval=0): + """ + Beam Blockette 400 (16 bytes) + Returns tuple + blk + Blockette type (UWORD, 2) + Next blockette's byte offset relative to fixed section of header + (UWORD, 2) + Beam azimuth (degrees) (FLOAT, 4) + Beam slowness (sec/degree) (FLOAT, 4) + Beam configuration (UWORD, 2) + Reserved (UBYTE, 2) + """ + self.infileseek(seekval) + fmtstr = self.fmt_order + "HHffH2B" + blk=self.sunpack(fmtstr, self.infileread(16)) + return list(blk) + +######################################################### + def Writeblk400(self, inblk, seekval=0, endianess=""): + """ + writes Beam Blockette 400 (16 bytes) + requires tuples inblk + inblk=(blkette, nextblk, baz,bslw,bconf,res0,res1) + """ + + try: + self.infileseek(seekval) + fmtorderstr=self.setEndian(endianess) + fmtstr = fmtorderstr + "HHffH2B" + + (blkette, nextblk, baz,bslw,bconf,res0,res1)=inblk + pack_blk=self.spack(fmtstr, blkette, nextblk, baz,bslw,bconf,res0,res1) + self.infilewrite(pack_blk) + return 16 + + except: + print "error writing blockette 400" + return 0 + +######################################################### + def blk405(self, seekval=0): + """ + Beam Delay Blockette 405 (6 bytes) + Returns tuple + blk + Blockette type (UWORD, 2) + Next blockette's byte offset relative to fixed section of header + (UWORD, 2) + Array of delay values (UWORD, 2) + """ + self.infileseek(seekval) + fmtstr = self.fmt_order + "HHH" + blk=self.sunpack(fmtstr, self.infileread(6)) + return list(blk) + +######################################################### + def Writeblk405(self, inblk, seekval=0, endianess=""): + """ + writes Beam Delay Blockette 405 (6 bytes) + requires tuples inblk + inblk=(blkette, nextblk, delay) + """ + + try: + self.infileseek(seekval) + fmtorderstr=self.setEndian(endianess) + fmtstr = fmtorderstr + "HHH" + + (blkette, nextblk, delay)=inblk + pack_blk=self.spack(fmtstr, blkette, nextblk, delay) + self.infilewrite(pack_blk) + return 6 + + except: + print "error writing blockette 405" + return 0 + +######################################################### + def blk500(self, seekval=0): + """ + Timing Blockette 500 (200 bytes) + Returns tuple + blk + Blockette type (UWORD, 2) + Next blockette's byte offset relative to fixed section of header + (UWORD, 2) + VCO correction (FLOAT, 4) + Time of exception (BTime expanded, 10) + microsec (UBYTE, 1) + Reception quality (UBYTE, 1) + Exception count (ULONG, 4) + Exception type (CHAR*16) + Clock model (CHAR*32) + Clock status (CHAR*128) + """ + self.infileseek(seekval) + fmtstr = self.fmt_order + "HHf" + blk1=self.sunpack(fmtstr, self.infileread(8)) + fmtstr = self.fmt_order + "HHBBBBH" + timeblk=self.sunpack(fmtstr,self.infileread(10)) + fmtstr = self.fmt_order + "BBI16s32s128s" + blk2=self.sunpack(fmtstr, self.infileread(182)) + + # incorporate timeblk tuple into blk list + blk = list(blk1) + blk.append(timeblk) + blk = blk + list(blk2) + + return blk + +######################################################### + def Writeblk500(self, inblk, seekval=0, endianess=""): + """ + writes Timing Blockette 500 (200 bytes) + requires tuples inblk + inblk=(blkette, nextblk, vcocorr,Year, Day, Hour, Min, Sec, junk, Micro, Micro2, \ + qual,cnt,type,mod,stat) + """ + + try: + self.infileseek(seekval) + fmtorderstr=self.setEndian(endianess) + fmtstr = fmtorderstr + "HHfHHBBBBHBBI16s32s128s" + + (blkette, nextblk, vcocorr,Year, Day, Hour, Min, Sec, junk, Micro, Micro2, \ + qual,cnt,type,mod,stat)=inblk + pack_blk=self.spack(fmtstr, blkette, nextblk, vcocorr,Year, Day, Hour, Min, Sec, junk, Micro, Micro2, \ + qual,cnt,type,mod,stat) + self.infilewrite(pack_blk) + return 200 + + except: + print "error writing blockette 500" + return 0 + +######################################################### + def blk1000(self, seekval=0): + """ + Data Only SEED Blockette 1000 (8 bytes) + Returns tuple + blk + Blockette type (UWORD, 2) + Next blockette's byte offset relative to fixed section of header + (UWORD, 2) + Encoding Format (BYTE, 1) + Word Order (UBYTE, 1) + Data Record Length (UBYTE, 1) + Reserved (UBYTE, 1) + """ + self.infileseek(seekval) + fmtstr = self.fmt_order + "HHbBBB" + blk=self.sunpack(fmtstr, self.infileread(8)) + return list(blk) + +######################################################### + def Writeblk1000(self, inblk, seekval=0, endianess=""): + """ + writes Data Only SEED Blockette 1000 (8 bytes) + requires tuples inblk + inblk=(blkette, nextblk, fmt,order,length,res) + """ + + try: + self.infileseek(seekval) + fmtorderstr=self.setEndian(endianess) + fmtstr = fmtorderstr + "HHbBBB" + + (blkette, nextblk, fmt,order,length,res)=inblk + pack_blk=self.spack(fmtstr, blkette, nextblk, fmt,order,length,res) + self.infilewrite(pack_blk) + return 8 + + except: + print "error writing blockette 1000" + return 0 + +######################################################### + def blk1001(self, seekval=0): + """ + Data Extension Blockette 1001 (8 bytes) + Returns tuple + blk + Blockette type (UWORD, 2) + Next blockette's byte offset relative to fixed section of header + (UWORD, 2) + Timing Quality (UBYTE, 1) + microsec (UBYTE, 1) + Reserved (UBYTE, 1) + Frame count (UBYTE, 1) + """ + self.infileseek(seekval) + fmtstr = self.fmt_order + "HHBBBB" + blk=self.sunpack(fmtstr, self.infileread(8)) + return list(blk) +######################################################### + def Writeblk1001(self, inblk, seekval=0, endianess=""): + """ + writes Data Extension Blockette 1001 (8 bytes) + requires tuples inblk + inblk=(blkette, nextblk, tqual,micro,res,fcnt) + """ + + try: + self.infileseek(seekval) + fmtorderstr=self.setEndian(endianess) + fmtstr = fmtorderstr + "HHBBBB" + + (blkette, nextblk, tqual,micro,res,fcnt)=inblk + pack_blk=self.spack(fmtstr, blkette, nextblk, tqual,micro,res,fcnt) + self.infilewrite(pack_blk) + return 8 + + except: + print "error writing blockette 1001" + return 0 + +######################################################### + def blk2000(self, seekval=0): + """ + Variable Length Opaque Data Blockette + Returns two tuples + blk, opaque + Blockette type (UWORD, 2) + Next blockette's byte offset relative to fixed section of header. Use 0 if no + more blockettes will follow. (UWORD, 2) + Total Blockette length kin Bytes (UWORD, 2) + Offset to Opaque Data (UWORD, 2) + Record number (ULONG, 4) + Data Word order (UBYTE, 1) + 0 = little endian (VAX or 80x86 byte order). + 1 = big endian (68000 or SPARC byte order). + Opaque Data flags (UBYTE, 1) + [bit 0] Opaque blockette orientation. + 0 = record oriented. + 1 = stream oriented. + [bit 1] Packaging bit. + 0 = Blockette 2000s from multiple SEED data records with different timetags + may be packaged into a single SEED data record. The exact original + timetag in each SEED Fixed Data Header is not required for each + blockette 2000. + 1= Blockette 2000s from multiple SEED data records with differing timetags + may NOT be repackaged into a single SEED data record. Set this bit + if the timetag in the SEED Fixed Data Header is required to properly + interpret the opaque data. + [bits 2-3] Opaque blockette fragmentation flags. + 00 = opaque record identified by record number is completely contained in + this opaque blockette. + 01 = first opaque blockette for record spanning multiple blockettes. + 11 = continuation blockette 2...N-1 of record spanning N blockettes. + 10 = final blockette for record spanning N blockettes. + [bits 4-5] File blockette information. + 00 = not file oriented. + 01 = first blockette of file. + 10 = continuation of file. + 11 = last blockette of file. + Number of Opaque Header fields (UBYTE, 1) + Opaque Data Header fields (VAR) + a Record type + b Vendor type + c Model type + d Software + e Firmware + Opaque Data Opaque - bytes of opaque data. Total length of opaque data + in bytes is blockette_length-15-length (opaque_data_header_string) + (Opaque) + """ + self.infileseek(seekval) + fmtstr = self.fmt_order + "HHHHIBBB" + blk=self.sunpack(fmtstr, self.infileread(15)) + #decompose tmpblk[6] into bit fields, create blk + tmpblk = self.UbytetoStr(blk, 6) + blk=tmpblk #recast blk as list + + #Now decipher Opaque Header + charlist=[] + char="" + length_data_string = 0 + NumOpaqueHeaders=int(blk[7]) + fmtstr = self.fmt_order + "s" + for i in range(NumOpaqueHeaders): + tmpchar="" + while tmpchar != "~": + tupchar=self.sunpack(fmtstr, self.infileread(1)) + tmpchar = str(tupchar[0]) + if tmpchar != "~": + char = char + tmpchar + length_data_string += 1 + charlist.append(char) + blk.append(charlist) +# opaque = "" +# rdbyte = int(tmpblk[2]) - 15 - length_data_string +# fmt = "=%ds" % rdbyte +# opaque=self.sunpack(fmt, self.infileread(rdbyte)) +# +# # +# print opaque +# # + return blk +######################################################### + def UbytetoStr(self, tup, offset): + """ + converts unsign byte to string values + """ + list=[] + strval = "" + # mask bit fields and build string + for i in range(8): + mask = 2**i + if tup[offset] & mask: + strval = "1" + strval + else: + strval = "0" + strval + + # build new list with decomposed bit string + for i in range(len(tup)): + if i == offset: + list.append(strval) + else: + list.append(tup[i]) + return list +######################################################### + def FlagStrtoInt(self, flagstr): + """ + converts Flag String to Integer + """ + flagint=0 + n=len(flagstr) - 1 + for a in flagstr: + if int(a): + flagint = flagint + 2**n + n-=1 + return flagint + +######################################################### + def Pad(self, var, num): + """ + pad header values to specified length with blank space + right justified + """ + varlength=len(var) + if varlength == num: + return var + elif varlength < num: + pad=num-varlength + newvar= var + SPACE*pad + return newvar + + +######################################################### +### END MSEED +######################################################### +class Segy(futils): + def __init__(self, infile): + self.type=self.serial=self.chan=self.time=self.rate=None + futils.__init__(self,infile) + # self.byteorder: byte order of input file. + # "big" + # "little" + # or "unknown" + self.byteorder = self.ByteOrder() + # self.fmt_order: companion to self.byteorder. Format string for stuct.pack + # ">" big endian + # "<" little endian + +######################################################### + def isSegy(self) : + """ + determines if processed file is segy or unknown type + """ + #if we don't know byteorder it must not be segy + if self.byteorder == "unknown": return 0 + + #otherwise it might be segy + (Sserial, Schan, Srate, Syear, Sday, Shour, Smin, Ssec) = self.idread() + #we have already tested for a valid time in self.ByteOrder, this test is + #to ensure that we were able to read a complete idhdr header + if Sserial == Schan == Srate == Syear == Sday == Shour == \ + Smin == Ssec == None: return 0 + #if we're this far it must be segy + self.type = "segy" + if Sserial < 36864: + self.serial = Sserial + else: + self.serial = self.to_hex(Sserial) + self.time = string.join(map(str, (Syear, Sday, Shour, Smin, Ssec)), ":") + self.chan = Schan + self.rate = Srate + return 1 + +######################################################### + def idhdr(self): + return self.type, self.serial, self.chan, self.time, self.rate + +######################################################### + def tracelength(self): + """ + returns trace length in seconds + """ + self.infile.seek(228) + fmtstr = self.fmt_order + "L" + numsamp=struct.unpack(fmtstr, self.infile.read(4))[0] + length=numsamp/self.rate + return length +######################################################### + def idread(self) : + """ + Read file as if SEGY file trying to extract + channel, time block, sample rate, and serial number + """ + try : + self.infile.seek(0) + SH=self.infile.read(240) +# self.infile.seek(12) + fmtstr = self.fmt_order + "L" +# chan = struct.unpack(fmtstr, self.infile.read(4))[0] +# print chan + chan=struct.unpack(fmtstr, SH[12:16])[0] + +# self.infile.seek(156) +# timeblock=self.infile.read(10) + fmtstr = self.fmt_order + "HHHHH" +# (year,day,hour,min,sec)=struct.unpack(fmtstr, timeblock) + (year,day,hour,min,sec)=struct.unpack(fmtstr, SH[156:166]) + +# self.infile.seek(200) + fmtstr = self.fmt_order + "L" +# samp=struct.unpack(fmtstr, self.infile.read(4))[0] +# print samp + samp=struct.unpack(fmtstr, SH[200:204])[0] + rate = int(1/(samp/1e6)) + +# self.infile.seek(224) + fmtstr = self.fmt_order + "H" +# serial = struct.unpack(fmtstr, self.infile.read(2))[0] + serial=struct.unpack(fmtstr, SH[224:226])[0] + + return serial, chan, rate, year, day, hour, min, sec + + except : + return None, None, None, None, None, None, None, None + +######################################################### + def ByteOrder(self, seekval=156) : + """ + read file as if it is segy just pulling time info + from header and determine if it makes sense unpacked + as big endian or little endian + """ + Order = "unknown" + try : + #seek to timeblock and read + self.infile.seek(seekval) + timeblock=self.infile.read(10) + + #assume big endian + (Year, Day, Hour, Min, Sec)=\ + struct.unpack('>HHHHH',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)=\ + struct.unpack('<HHHHH',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 = "<" + except : + pass + + return Order +######################################################### + def to_int(self, input) : + """ + conversion routine from hex to integer + """ + HEXCHAR=["0","1","2","3","4","5","6","7","8","9","A","B","C","D","E","F"] + hexnum=str(input) + retval=0 + input_length=len(hexnum) + for i in range(input_length) : + for index in range(len(HEXCHAR)) : + if string.upper(hexnum[i]) == HEXCHAR[index] : + retval=retval + index*(16**(input_length-(1+i))) + return retval + +######################################################### + def to_hex(self, number) : + """ + conversion routine from integer to hex + """ + retval=0 + hexnum=hex(number) + return hexnum[2:] +######################################################### +### END SEGY +######################################################### +if __name__ == "__main__" : + + VERSION = "2008.204" + filecnt=0 +# based on traverse routine in "python standard library", Lundh pg 34 + def GetTrace(DataDir) : + global filecnt + stack = [] + rwError=0 + for k in range(len(DataDir)) : + stack.append(DataDir[k]) + files = [] + file_list = {} + errcnt=0 + while stack : + directory = stack.pop() + if not os.path.isdir(directory): + print "\n***WARNING*** Directory \"%s\" not found.\n" % directory + continue + # else : + for file in os.listdir(directory): + fullname = os.path.join(directory, file) + if not os.access(fullname, 6): + rwError+=1 + continue + + if os.path.isfile(fullname) : + filecnt+=1 + try: + newfile = Segy(fullname) + if newfile.isSegy(): + (type, serial, chan, time, rate) = newfile.idhdr() + length = newfile.tracelength() + # length = "NULL" + file_list[fullname] = (type, serial, chan, time, rate, length, newfile.ByteOrder()) + newfile.close() + except: + rwError+=1 + pass + else: + try: + newfile=Mseed(fullname) + if newfile.isMseed(): + try: + #simple test to determine if correct size file + filesize=newfile.filesize + blksize=newfile.blksize + (numblocks, odd_size)=divmod(filesize, blksize) + if odd_size: + rwError+=1 + continue + except: + rwError+=1 + continue + + type=newfile.type + serial=string.strip(newfile.FH.Stat) + chan=string.strip(newfile.FH.Chan) + loc=string.strip(newfile.FH.Loc) + net=string.strip(newfile.FH.Net) + time=newfile.time + rate=newfile.rate + length = newfile.tracelength() + # length = "NULL" + file_list[fullname] = (type, serial, chan, time, rate, length, newfile.ByteOrder()) + newfile.close() + except: + rwError+=1 + pass + # if os.path.isdir(fullname) and not os.path.islink(fullname) : + if os.path.isdir(fullname) or (os.path.islink(fullname) and not os.path.isfile(fullname)): + stack.append(fullname) + return file_list, rwError + + if len(sys.argv) > 1 : + if sys.argv[1] == "-#" : + print VERSION + sys.exit(1) + else : + print "Unknown arguement %s" % sys.argv[1:] + sys.exit(1) + + segynum = 0 + mseednum = 0 + unknownnum = 0 + Dirs = [] +# Dirs.append(",") + Dirs.append(".") +# Dirs.append("/Users/bruce/data/uniq") + (file_list,rwError) = GetTrace(Dirs) + for file in file_list.keys() : + file_type = file_list[file][0] + if file_type == "segy" : + segynum += 1 +# print "\n SEGY \n" +# print "segy: ", file_list[file][1], file_list[file][2], file_list[file][3], \ +# file_list[file][4], file_list[file][5], file_list[file][6] + elif file_type == "mseed" : +# if file_type == "mseed" : + mseednum += 1 +# print "\n MSEED \n" +# print "mseed: ", file_list[file][1], file_list[file][2], file_list[file][3], \ +# file_list[file][4], file_list[file][5], file_list[file][6] + + #else : + #unknownnum += 1 + #print "\n Format Not Recognized \n" + print "\nTotal Files Found = %i " % filecnt + print "SEGY Files Processed = %i \nMSEED Files Processed = %i" % (segynum, mseednum) + print "Total RWErrors = %i " % rwError + print "Total Files Identified = %i" % len(file_list) diff --git a/fixhdr/Makefile b/fixhdr/Makefile deleted file mode 100644 index 66f21fdf0d6be03fb274e3f64181ab198506f922..0000000000000000000000000000000000000000 --- a/fixhdr/Makefile +++ /dev/null @@ -1,46 +0,0 @@ -# Requires sourcing the setup information in setup_passoft - -EXEC = fixhdr -PYEXEC = fixhdr.py - -MODLIB = lib/python/$(EXEC) - -all: - -install: - @$(PASSINSTALL) -d -m 0755 $(PASSCAL)/$(MODLIB) - @$(PASSCHEXE) $(PYEXEC) - @$(PASSCP) $(PYEXEC) $(PASSCAL)/$(MODLIB) - @$(PASSCHEXE) $(EXEC) - @$(PASSCP) $(EXEC) $(PASSCAL)/bin - @$(PASSINSTALL) -d -m 0755 $(PASSCAL)/man/manl - @$(PASSCP) $(EXEC).man $(PASSCAL)/man/manl/$(EXEC).l - -installl: - @$(PASSINSTALL) -d -m 0755 $(PASSOFT)/$(MODLIB) - @$(PASSCHEXE) $(PYEXEC) - @$(PASSCP) $(PYEXEC) $(PASSOFT)/$(MODLIB) - @$(PASSCHEXE) $(EXEC) - @$(PASSCP) $(EXEC) $(PASSOFT)/bin/$(EXEC) - -tarsrc: - @$(PASSINSTALL) -d -m 0755 $(PASSCAL)/tar - @$(PASSCP) $(PASSOFT)/src/setup_passoft . - @$(PASSTAR) cf $(EXEC)src.tar setup_passoft Makefile \ - $(EXEC) *.man *.py - @$(PASSCP) $(EXEC)src.tar $(PASSCAL)/tar - -tarbin: - @$(PASSINSTALL) -d -m 0755 $(PASSCAL)/tar - @$(PASSCP) $(PASSOFT)/src/setup_passoft . - @$(PASSTAR) cf $(EXEC)bin.tar setup_passoft Makefile \ - $(EXEC) *.man *py - @$(PASSCP) $(EXEC)bin.tar $(PASSCAL)/tar - -clean: - @$(PASSRM) *~ - -uninstall: - @$(PASSRM) $(PASSCAL)/bin/$(EXEC) - @$(PASSRM) -r $(PASSCAL)/$(MODLIB) - @$(PASSRM) $(PASSCAL)/man/manl/$(EXEC).l diff --git a/fixhdr/fixhdr b/fixhdr/fixhdr deleted file mode 100755 index 54bde8d78532dfa2409e507606b59dee6e4400a0..0000000000000000000000000000000000000000 --- a/fixhdr/fixhdr +++ /dev/null @@ -1,28 +0,0 @@ -#!/bin/bash -# Launch fixhdr.py - -if [ ! -z "${PASSCAL}" ] -then - PYTHON=${PASSCAL}/other/bin/picpython - export PYTHON - PYTHONPATH=${PASSCAL}/lib:${PASSCAL}/lib/python/libtrace - export PYTHONPATH -else - echo "PASSCAL environment variable not set! Cannot continue." - exit 1 -fi - -if [ $# = 0 ] -then - echo "Starting fixhdr..." - ${PYTHON} ${PASSCAL}/lib/python/fixhdr/fixhdr.py $* & - exit 0 -fi -if [ $1 = "-#" ] -then - ${PYTHON} ${PASSCAL}/lib/python/fixhdr/fixhdr.py $* - exit 0 -fi -echo "Starting fixhdr..." -${PYTHON} ${PASSCAL}/lib/python/fixhdr/fixhdr.py $* & - diff --git a/fixhdr/fixhdr.py b/fixhdr/fixhdr.py index 3ea7f8ccdc2bfec717820c6a01f6bc8bd8747646..f93bb34376830bb1a4f7e9fbdfa722958aef5953 100755 --- a/fixhdr/fixhdr.py +++ b/fixhdr/fixhdr.py @@ -46,7 +46,7 @@ # author: bcb # date 2005.027 # -# added try/except for directory & file open in self.FindTrace to +# added try/except for directory & file open in self.FindTrace to # reflect changes in LibTrace.py # Optimized FindTrace # @@ -75,7 +75,7 @@ # FillTime # ClearAllTime # SetFlag -# +# # Endianess notebook # allows user to switch endianess of data headers # associated functions: @@ -99,7 +99,7 @@ # functions: # FlushLogs # DisplayLogs -# +# ########################## # modification # author: bcb @@ -181,7 +181,7 @@ # reworked methods # LaunchApplyTimeCor, ApplyTimeCor, UpdateTimeShift, GetStartEnd, writeFile, LoadTemplate, ExitCheck # wait, WriteLog, addTextInfoBar -# +# # removed methods # WaitWidget, ModAndExit # @@ -550,7 +550,7 @@ class MainWindow: if BATCHMODE: #address endian change first since unique i.e. no batchfile print "Running in Batch Mode" - if RunEndianBatch: + if RunEndianBatch: print "Changing Endianess to: ", Endianess self.ToEndian.set(Endianess) self.RunChangeEndian.set(1) @@ -564,7 +564,7 @@ class MainWindow: print "Using Template: ", self.BatchFile.get() self.LoadTemplate() if RunTimeBatch: - if not self.UpdateTimeDict: + if not self.UpdateTimeDict: print "No Timing Corrections in Template.\nDone" sys.exit(1) print "Finding Files beneath directory: ", self.DataDirs.get() @@ -572,7 +572,7 @@ class MainWindow: self.RunApplyTimeCor.set(1) self.ApplyTimeCor() if RunHeaderBatch: - if not self.UpdateHdrDict: + if not self.UpdateHdrDict: print "No Header Corrections in Template.\nDone" sys.exit(1) print "Finding Files beneath directory: ", self.DataDirs.get() @@ -2251,7 +2251,7 @@ class MainWindow: # This block of code should be activated. What needs to happen is that for every # blockette that has a BTime entry, that entry should be shifted based on the above - # logic. + # logic. # numblk=hdrs[3][3] # addseek=hdrs[3][6] # b=0 @@ -2323,7 +2323,7 @@ class MainWindow: # newshift = self.TSShift.get() + oldshift # else: # newshift = self.TSShift.get() -# +# # NewShiftmSec = newshift/0.0001 # if -2**31 >= NewShiftmSec or NewShiftmSec >= (2**31)-1 : # err= "Time Correction exceeds limits of field [-2**31 <= TimeCorr <= (2**31)-1]" @@ -2530,7 +2530,7 @@ class MainWindow: #local variables LogVarget=self.LogVar.get LogTextinsert=self.LogText.insert - + self.LogText.clear() if LogVarget() == 0: textlist=self.LogAll elif LogVarget() == 1: textlist=self.LogHeader @@ -2564,7 +2564,7 @@ class MainWindow: self.ToEndian.set("big") else: self.ToEndian.set("little") - #launch thread + #launch thread #eRun=threading.Thread(target=self.ChangeEndian) self.ChangeEndian() #eRun.start() @@ -2635,7 +2635,7 @@ class MainWindow: typenxt=msfile.typenxt GetBlk=msfile.GetBlk PutBlk=msfile.PutBlk - + filesize=msfile.filesize blksize=msfile.blksize (numblocks, odd_size)=divmod(filesize, blksize) @@ -2780,7 +2780,7 @@ class MainWindow: self.TraceList_tl = Toplevel(master) # self.TraceList_tl.title("Trace Listing") - self.ExitTraceList_b = Button(self.TraceList_tl, + self.ExitTraceList_b = Button(self.TraceList_tl, activebackground = 'red', cursor = 'pirate', background = 'lightblue', @@ -2832,7 +2832,7 @@ class MainWindow: self.TimeCorList_tl = Toplevel(master) self.TimeCorList_tl.title("Time Corrections") - self.ExitTimeCorList_b = Button(self.TimeCorList_tl, + self.ExitTimeCorList_b = Button(self.TimeCorList_tl, activebackground = 'red', cursor = 'pirate', background = 'lightblue', @@ -2898,7 +2898,7 @@ class MainWindow: """ #local variables stringjoin=string.join - + FileCorDict={} timeid = self.TimeShiftName.get() (keystat,keyloc,keynet)=string.split(timeid, ":") @@ -3071,7 +3071,7 @@ class MainWindow: self.addTextInfoBar(" " + var + "...", 'orange') if not self.SetHdrDict.has_key(var): self.ExitWidget(self.root) - return + return else: for subkey in self.UpdateHdrDict[var].keys(): if not self.SetHdrDict[var].has_key(subkey): @@ -3082,7 +3082,7 @@ class MainWindow: ################################################################## def ExitWidget(self,master): """ - Allow user to apply UpdateHdrDict entries that have not + Allow user to apply UpdateHdrDict entries that have not been applied """ # first see if we got here from ExitCheck. If so kill window @@ -3131,8 +3131,8 @@ class MainWindow: relief = 'groove', borderwidth=2 ) - self.ExitEntry_fm.pack(side='left', - fill='x', + self.ExitEntry_fm.pack(side='left', + fill='x', pady=5 ) @@ -3316,8 +3316,8 @@ command= Command(self.KillCancelTL, runvar) relief = 'groove', borderwidth=2 ) - self.Text_fm.pack(side='top', - fill='x', + self.Text_fm.pack(side='top', + fill='x', pady=5 ) @@ -3402,7 +3402,7 @@ command= Command(self.KillCancelTL, runvar) ) self.NewOnly_b.pack(side='bottom', fill='x') - elif action == "undo": + elif action == "undo": self.WarnWidgetDismiss_b = Button(self.WarnWidget_tl, text="Ignore & Undo Corrections", cursor='pirate', @@ -3416,8 +3416,8 @@ command= Command(self.KillCancelTL, runvar) relief = 'groove', borderwidth=2 ) - self.Text_fm.pack(side='top', - fill='x', + self.Text_fm.pack(side='top', + fill='x', pady=5 ) if action == "undo": @@ -3529,8 +3529,8 @@ command= Command(self.KillCancelTL, runvar) relief = 'groove', borderwidth=2 ) - self.saveWidgetEntry_fm.pack(side='left', - fill='x', + self.saveWidgetEntry_fm.pack(side='left', + fill='x', pady=5 ) @@ -3686,7 +3686,7 @@ command= Command(self.KillCancelTL, runvar) newline = inputfile.readline() if not newline : break if newline[0] == "#" or newline[:-1] == "": continue - if newline[0] == "}": + if newline[0] == "}": print "ERROR in Template File Format" if newline[:7] == "hdrlist" : while 1: @@ -3794,10 +3794,10 @@ command= Command(self.KillCancelTL, runvar) """ Populate Help NoteBook """ - + self.Help_nb = master.add('Help') - self.TemplateFormat_b = Button(self.Help_nb, + self.TemplateFormat_b = Button(self.Help_nb, activebackground = 'green', background = 'lightblue', text="Template Format", @@ -4039,7 +4039,7 @@ command= Command(self.KillCancelTL, runvar) self.helpFileFormat_tl = Toplevel(master) self.helpFileFormat_tl.title("Template Format") - self.ExitFileHelp_b = Button(self.helpFileFormat_tl, + self.ExitFileHelp_b = Button(self.helpFileFormat_tl, activebackground = 'red', cursor = 'pirate', background = 'lightblue', @@ -4059,7 +4059,7 @@ command= Command(self.KillCancelTL, runvar) FileFormatTextinsert("end", """ The Template file consists of two lists: """) - FileFormatTextinsert("end", "hdrlist", "highlight") + FileFormatTextinsert("end", "hdrlist", "highlight") FileFormatTextinsert("end", """ and """) FileFormatTextinsert("end", "timelist", "highlight") FileFormatTextinsert("end", """. @@ -4067,12 +4067,12 @@ command= Command(self.KillCancelTL, runvar) and are denoted by a '#' in the first column. For the """) - FileFormatTextinsert("end", "hdrlist", "highlight") + FileFormatTextinsert("end", "hdrlist", "highlight") FileFormatTextinsert("end", """ the columns are: stat:chan:loc:net:sps stat:chan:loc:net For the """) - FileFormatTextinsert("end", "timelist", "highlight") + FileFormatTextinsert("end", "timelist", "highlight") FileFormatTextinsert("end", """ the columns are: sta:loc:net Start_Time End_Time Shift(s) Time_Tag Corr_Type sta:loc:net yyyy:ddd:hh:mm:ss yyyy:ddd:hh:mm:ss float/NA set/unset/NA add/replace/NA @@ -4447,11 +4447,17 @@ packed into records until a message falls into a new minute. Log records have no blockettes, so the strings start at offset 48.""") ################################################################## -if BatchFile: - mw = MainWindow("fixhdr %s" % VERSION, BatchFile) -else: - mw = MainWindow("fixhdr %s" % VERSION) - mw.root.geometry("650x400") - mw.root.mainloop() -# import profile -# profile.run("mw.root.mainloop()") + +def main(): + if BatchFile: + mw = MainWindow("fixhdr %s" % VERSION, BatchFile) + else: + mw = MainWindow("fixhdr %s" % VERSION) + mw.root.geometry("650x400") + mw.root.mainloop() + # import profile + # profile.run("mw.root.mainloop()") + +if __name__ == '__main__' : + main() + diff --git a/requirements_dev.txt b/requirements_dev.txt deleted file mode 100644 index a253cec2f07d3bb4b8610c047d2358dabfcd82af..0000000000000000000000000000000000000000 --- a/requirements_dev.txt +++ /dev/null @@ -1,11 +0,0 @@ -pip==9.0.1 -bumpversion==0.5.3 -wheel==0.30.0 -watchdog==0.8.3 -flake8==3.5.0 -tox==2.9.1 -coverage==4.5.1 -Sphinx==1.7.1 -twine==1.10.0 - - diff --git a/setup.py b/setup.py index a72fbba94165c2081aba599109289968d1494dc2..5a451add87e851362a3b08c198cdc54c789cd77b 100644 --- a/setup.py +++ b/setup.py @@ -22,14 +22,27 @@ setup( 'Natural Language :: English', 'Programming Language :: Python :: 2.7', ], - description="Modifies mseed headers.", + description="Modifies mseed headers", entry_points={ 'console_scripts': [ 'fixhdr=fixhdr.fixhdr:main', ], }, - install_requires=[], + install_requires=['Pmw'], setup_requires = [], + extras_require={ + 'dev': [ + 'pip', + 'bumpversion', + 'wheel', + 'watchdog', + 'flake8', + 'tox', + 'coverage', + 'Sphinx', + 'twine', + ] + }, license="GNU General Public License v3", long_description=readme + '\n\n' + history, include_package_data=True, diff --git a/tox.ini b/tox.ini index ba176443d41b800abf882f3cca08bd7e443b8b7b..5482bdfbbba7662cec49153b3c31307252b7e8d1 100644 --- a/tox.ini +++ b/tox.ini @@ -1,9 +1,10 @@ [tox] -envlist = py27, flake8 +envlist = py27, py36 flake8 [travis] python = 2.7: py27 + 3.6: py36 [testenv:flake8] basepython = python @@ -13,6 +14,6 @@ commands = flake8 fixhdr [testenv] setenv = PYTHONPATH = {toxinidir} -python setup.py test +commands=python setup.py test