加载中…
个人资料
  • 博客等级:
  • 博客积分:
  • 博客访问:
  • 关注人气:
  • 获赠金笔:0支
  • 赠出金笔:0支
  • 荣誉徽章:
正文 字体大小:

SAC文件在FORTRAN和C中的读写

(2011-07-28 21:32:22)
标签:

sac

fortran

c

子程序

rsac

wsac

教育

SAC读写子程序
你可以利用SAC子程序库让自己的C或FORTRAN程序可以读写SAC格式的文件。这个库叫做sacio.a,位于sac下的lib目录中。当编译或者连接你的代码的时候,需要将sacio.a包含进去以使用下面即将讨论的子程序。
在SAC库中有两个子程序可以使你在自己的C或FORTRAN程序中读取SAC格式的文件。
   - RSAC1 读取等间隔文件
   - RSAC2 读取不等间隔文件和谱文件
还有一些字程序帮助你在读入文件之后获得、设置头段变量的值:
   - GETFHV、SETFHV 获得、设置浮点型头段变量值
   - GETIHV、SETIHV 获取、设置用整型枚举的字符串头段变量值
   - GETKHV、SETKHV 获取、设置字符串头段变量值
   - GETLHV、SETLHV 获得、设置逻辑型头段变量值(declared as long in C)
   - GETNHV、SETNHV 获取、设置整型头段变量值
还有三个用于将SAC数据写入磁盘的子程序:
   - WSAC1 写入等间隔文件
   - WSAC2 写入不等间隔文件
   - WSAC0 写入比另外两个包含更多头段信息的SAC文件(如果是新文件,那么它首先需要使用一个新的子程序NEWHDR来定义头段变量。如果是写入一个已经存在的文件则不应该调用NEWHDR,在写入这样一个文件之前,它要更新如DEPMAX和BAZ这样的头段变量)
最后,有一个子程序叫做NEWHDR,用于初始化所有的SAC头段变量为未定义状态,这个子程序和WSAC0连用。

读取一个等间隔采样的SAC文件:
大多数的SAC数据都是等间隔的,因而这个字程序很常用。使用rsac1()时,子程序将返回采样间隔、开始时间和振幅数据,剩下的头段变量值将一直储存在内存中等待稍后的使用直到下一次调用rsac1()。
下面给出在C和FORTRAN中调用rsac1()的简单例子,这些例子位于sac/doc/examples目录下。

Fortran rsac1f.f:

      program rsac
      implicit none

      integer MAX
      parameter (MAX=1000)

      real yarray
      dimension yarray(MAX)

      real beg, del
      integer nlen
      character*10 KNAME
      integer nerr

      kname = 'FILE1'

      call rsac1(kname, yarray, nlen, beg, del, MAX, nerr)
      if(nerr .NE. 0) then
            write(*,*)'Error reading in file: ',kname
      call exit(-1)
      endif

    Do some processing...

      call exit(0)
      end
程序定义了一个实型数组yarray(MAX),该数组用于储存读入的文件中每个数据点的振幅值,MAX为读入的最大点数,此处为1000个数据点。要读入的文件名为“FILE1”。
调用rsac1()时需要很多参数,这些参数在使用之前需要被定义:
  -kname 要读入的文件名
  -yarray 数据被载入yarray数组中
  -nlen 数组长度
  -beg 数据开始时间
  -del 数据采样间隔
  -MAX 所得读入的最大数据点数,如果文件数据点数超过MAX,剩下的不会被读取(个人揣测)
  -nerr 错误返回标记,0代表成功,非零代表失败。在调用子程序之后检查nerr的值是很必要的,这可以帮助你减少不必要的麻烦。
如果想试试编译并执行这个程序,你还需要做下面一些准备:整个程序只是读取文件,并没有其他操作和输出,为了更好的检验结果不妨在call exit(0)之前输入类似于print yarray,nlen,beg,del,MAX,nerr之类的简单语句;另外要读取的文件FILE1还没有呢,可以用FUNCGEN函数生成一个地震数据,然后以FILE1(注意大小写一致)为文件名写入磁盘即可。可以使用原来曾经使用过的命令来编译这个文件:
  gfortran -o rsac1 rsac1f.f /usr/local/sac/lib/sacio.a
当然还有更通用一点的办法:
  gfortran -o rsac1 rsac1f.f `sac-config --cflags --libs sacio sac`
注意命令中有一对倒引号,另外这个命令牵涉到的一个脚本sac-config有些问题,需要修改才可使用,关于sac-config的修改以及使用请参见程序编译章节。

C rsac1c.c :

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#include <sacio.h>


#define MAX 1000

int main(int argc, char **argv)
{
 
  float yarray[MAX], beg, del;
  int nlen, nerr, max = MAX;
  char kname[ 11 ] ;
 
 
  strcpy( kname , "FILE1" ) ;
 

 
  rsac1( kname, yarray, &nlen, &beg, &del, &max, &nerr, strlen( kname ) ) ;

 
  if ( nerr != 0 ) {
    fprintf(stderr, "Error reading in SAC file: %s\n", kname);
    exit ( nerr ) ;
  }
 
 
 
  exit(0);
}
C程序与上面的FORTRAN程序大体相同,注意在调用rsac1()时在nerr后需要一个额外的参数,这是一个字符串长度标记符,它指明了字符串kname的长度,这个长度不包含字符串结尾的结束标志"\0"。注意除了字符串长度标记符之外的其它参数都是传引用方式传递的。编译的方法与FORTRAN程序相似,但是有所有不同,gcc -o program rsac1c.c -lm `sac-config --cflags --libs sacio sac`

读取非等间隔数据文件:
子程序rsac2()用的不是很多。在调用rsac2()时时间和幅度数据都被返回并储存起来。不像rsac1(),起始时间和时间间隔并不会被返回因为这些信息随着时间数据一起被返回了。
Fortran rsac2f.f:

      program rsac_2
      implicit none

      integer MAX
      parameter (MAX=3000)

      real xarray, yarray
      dimension xarray(MAX), yarray(MAX)

      character*10 kname
      integer nlen
      integer nerr    
      kname='file2'

      call rsac2(kname,yarray,nlen,xarray,MAX,nerr)

      if(nerr .ne. 0) then
         write(*,*)'error reading in sac file: ',kname
         call exit(-1)
      endif

    Do some processing ....

      call exit(0)
      end
从程序可以看出rsac2()和rsac1()的主要区别在于起始时间以及采样间隔被时间数组xarray所取代了。编译方法类似~程序的C版本不再赘述,有需要可以查看源代码。

获取头段变量:
在使用rsac1()或rsac2()读入文件之后,数据已经保存在数组中,但是大部分的头段变量的信息都在内存中,因而需要根据头段变量的类型不同选择相应的子程序去读取头段变量值。
Fortran gethvf.f:

      program rsac
      implicit none

      integer max
      parameter (MAX=1000)

      real yarray
      dimension yarray(MAX)
      
      character*10 kname
      integer nlen
      real beg, del
      integer nerr
      integer n1, n2
      real delta, b, t1, t2    
      kname='file1'

      call rsac1(kname,yarray,nlen,beg,del,MAX,nerr)
   
      if(nerr .ne. 0) then
         write(*,*)'Error reading SAC file: ',kname
         call exit(-1)
      endif

    Get floating point header value: Delta
       'delta' - name of the header variable requested
       delta   - value of the header variable delta, returned
       nerr    - Error return flag
      call getfhv('delta',delta,nerr)
      if(nerr .ne. 0) then
         write(*,*)'Error reading variable: delta'
         call exit(-1)
      endif

    Get floating point header value: B
      call getfhv('b',b,nerr)
      if(nerr .ne. 0) then
         write(*,*)'Error reading variable: b'
         call exit(-1)
      endif

    Get floating point header value: t1
      call getfhv('t1',t1,nerr)
      if(nerr .ne. 0) then
         write(*,*)'Error reading variable: t1'
         call exit(-1)
      endif

    Get floating point header value: t2
      call getfhv('t2',t2,nerr)
      if(nerr .ne. 0) then
         write(*,*)'Error reading variable: t2'
         call exit(-1)
      endif

    Compute the time sample at which t1 and t2 occur
      n1 = int((t1 - b) / delta)
      n2 = int((t2 - b) / delta)

    ......

      call exit(0)
      end
整个程序的前部分就是读取sac文件,前面已经说过,后半部分调用子程序getfhv分别读取四个浮点型数据,可以看到,子程序getfhv()有3个参数,第一个参数为字符串,代表要读取的头段变量名,第二个参数将接收到头段变量值,第三个参数则是错误返回标记。可以想见,其他的获取头段变量值的子程序应该与此类似~程序的C版本不再赘述。

写等间隔SAC文件:
Fortran  wsac1f.f:

      program wsac
      implicit none

      integer MAX
      parameter (MAX=200)

      real yfunc
      dimension yfunc(MAX)

      character*10 kname
      integer j
      integer nerr
      real beg
      real del
      real x

    Define the file to be written, the beginning time
    time sampling, and the initial value
      kname = 'expdata'
      beg   = 0.00
      del   = 0.02
         = beg

    Create the Amplitude data, an Exponential
      do j=1,MAX
         yfunc(j)=exp(-x)
         x=x+del
      enddo

    Write the SAC file kname
      - kname holds the name of the file to be written
      - yfunc Input Amplitude data
      - MAX number of points to be written
      - beg Beginning Time of the data
      - del Time Sampling of the series
      - nerr Error return Flag
      call wsac1(kname,yfunc,MAX,beg,del,nerr)
      
      if(nerr .NE. 0) then
         write(*,*)'Error writing SAC File: ', kname
         call exit(-1)
      endif

      call exit(0)
      end
程序创建了一个200个数据的数组yfunc(),并通过调用wsac1()将值写入文件中,wsac1()的参数与rsac1()类似。程序的C版本不再赘述。

写非等间隔文件:
自己查找程序的FORTRAN和C版本。

写包含更多头段信息的SAC文件:
为了创建一个拥有比wsac1和wsac2更多头段信息的文件,你需要使用一系列的子程序去设置头段变量然后使用wsac0,下面有三个例子,其中第一个有点像wsac2:
FORTRAN非等间隔数据:
wsac3f.f:

      program wsac3f
      implicit none

      integer MAX
      parameter (MAX=300)
      
      real xdata, ydata
      dimension xdata(MAX), ydata(MAX)

      character*10 kname
      integer j
      integer nerr
      real cona, conb

    Set the name the file to be written and initial x value
      kname='expdata    '
      xdata(1) = 0.1
      cona     = 12.3
      conb     = -45.6

    Create the Amplitude and Time, an Exponential
    Best viewed with axis as loglin
      ydata(1) = exp(-xdata(1))
      do j=2,MAX
         xdata(j) = xdata(j-1) + xdata(j-1) * 1.0/(4.0 * 3.1415);
         ydata(j) = exp(-xdata(j))
      enddo

    Create a New Header to store more information
    Newly created header value are set to a default state      
      call newhdr()
      
    Store values in the newly created header
    You must define the following header variables
       - delta  Time Sampling
                Only if the file is evenly spaced
       - b      Beginning Time
       - e      Ending Time
       - npts   Number of Points in the File
       - iftype File Type
            - itime Time Series File
            - irlim Spectral File Real/Imaginary
            - iamph Spectral File Amplitue/Phase
            - ixy   X-Y File
            - iunkn Unknown
!
    All other variables are up to the user
      call setnhv('npts',    max,        nerr)
      call setlhv('leven',   .false.,    nerr)
      call setfhv('b',       xdata(1),   nerr)
      call setfhv('e',       xdata(max), nerr)
      call setihv('iftype',  'ixy',      nerr)
      call setfhv('user0',   cona,       nerr)
      call setfhv('user1',   conb,       nerr)
      call setkhv('kuser0', 'gendat',    nerr)
      
    Write the SAC file kname
      - kname holds the name of the file to be written
      - xdata Input Time Data      
      - yfunc Input Amplitude Data
      - nerr Error return Flag
      call wsac0(kname,xdata,ydata,nerr)

    Check the Error status
      - 0 on Success
      - Non-Zero on Error
      if(nerr .NE. 0) then
         write(*,*)'Error writing SAC File: ', kname,nerr
         call exit(-1)
      endif

      call exit(0)

      end
在调用wsac0之前首先要调用newhdr子程序,创建一个新的头段,所有的头段变量均初吴缺省状态或未定义状态。SAC的众多头段变量中delta、b、e、npts、iftype是必须赋值的,通过使用各种set?hv的命令来实现头段变量的赋值。C版本不再赘述。

FORTRAN XYZ(3-D)文件
wsac4f.f:

      program wsac
      implicit none
      
    Maximum Size of Array, in 2-D
      integer MAX
      parameter (MAX=36)

    Size of arrays to store the data
      real dummy, zdata
      dimension dummy(MAX), zdata(MAX)
      
    Define variables to be passed into wsac0()
      character*10 kname
      integer i, j, k
      integer nerr
      integer nx, ny
      real minimum, maximum

    Define the file to be written and the min and max of the 2-D Array
      kname   = 'xyzdata'
      minimum = 1.0
      maximum = 6.0
      nx      = 6
      ny      = 6
      
      ! Create the 2D Data
      k = 1
      do i = 1,nx
         do j = 1,ny
            zdata(k) = sqrt(j * 1.0 * j + i * 1.0 * i)
            k = k + 1
         enddo
      enddo

      ! Create a new Header and fill it
       We are defining the data type, iftype to be 'ixyz', a 2-D Array
      call newhdr
      call setnhv('npts',     MAX,     nerr)
      call setlhv('leven',    .true.,  nerr)
      call setihv('iftype',   'ixyz',  nerr)
      call setnhv('nxsize',   nx,      nerr)
      call setnhv('nysize',   ny,      nerr)
      call setfhv('xminimum', minimum, nerr)
      call setfhv('xmaximum', maximum, nerr)
      call setfhv('yminimum', minimum, nerr)
      call setfhv('ymaximum', maximum, nerr)

    Write the SAC file kname
      - kname holds the name of the file to be written
      - dummy Input Amplitude Data
      - zdata Input Time Data      
      - nerr Error return Flag
      call wsac0(kname,dummy,zdata,nerr)

    Check the Error status
      - 0 on Success
      - Non-Zero on Error
      if(nerr .NE. 0) then
         write(*,*)'Error writing SAC File: ', kname,nerr
         call exit(-1)
      endif

      call exit(0)

      end

FORTRAN等间隔文件
wsac5f.f:

    program wsac5f
    implicit none
    
    integer NCOMP
    parameter(NCOMP=11)

    integer NDATA
    parameter(NDATA=4000)

    real sdata(NDATA,NCOMP+1), xdummy(NDATA)
    CHARACTER KNAME(NCOMP+1)*10
    real evla, evlo, stla, stlo
    character*11 kevnm, kstnm
    real b, delta
    real cmpaz, cmpinc
    integer npts
    integer nerr, j, i

    DATA KNAME/'STAZ','STBZ','STCZ','STDZ','STEZ',
              'STFZ','STGZ','STHZ','STHN','STHE','STHN','STNQ' /    

         = 0.0
    delta  = 0.25
    cmpaz  = 0.0
    cmpinc = 0.0
    npts  = NDATA
    evla   = -23.56
    evlo   = 123.56
    
    call newhdr () ;
    call setihv("IFTYPE", "ITIME", nerr)
    call setihv("IZTYPE", "IB",    nerr)
    call setfhv("B",      b,       nerr)
    call setlhv("LEVEN",  .TRUE.,  nerr)
    call setfhv("DELTA",  delta,   nerr)
 
    kevnm = "Event Name"
 
    call setnhv("NPTS",   npts,  nerr)
    call setfhv("EVLA",   evla,   nerr)
    call setfhv("EVLO",   evlo,   nerr)
    call setkhv("KEVNM",  kevnm,  nerr)
    call setfhv("CMPAZ",  cmpaz,  nerr)
    call setfhv("CMPINC", cmpinc, nerr)

    do j = 1,NCOMP-2
       kstnm = kname(j)  
       call setkhv ( "KSTNM", kstnm, nerr)
       stla = j * 10
       stlo = j * 20
       do i = 1,NDATA
          sdata(i,j) = 1.0 * rand()
       enddo
       call setfhv ( "STLA" , stla , nerr )
       call setfhv ( "STLO" , stlo , nerr )
       call wsac0 ( kstnm, xdummy, sdata(1,j), nerr)
    enddo
 
    cmpinc = 90.0
    call setfhv("CMPINC", cmpinc, nerr)
    j = 9
    do i = 1,NDATA
       sdata(i,j) = 1.0 * rand()
    enddo
    call wsac0(kname(9), xdummy, sdata(1,9), nerr)
 
    cmpaz = 90.0
    call setfhv("CMPAZ", cmpaz, nerr)
    j = 10
    do i = 1,NDATA
       sdata(i,j) = 1.0 * rand()
    enddo
    call wsac0(kname(10), xdummy, sdata(1,10), nerr)
    
    end

--------------------------------------------------------------
翻译自sac/aux/help/user_man/input_output
源代码位于sac/doc/examples中
2011-07-28

0

阅读 收藏 喜欢 打印举报/Report
  

新浪BLOG意见反馈留言板 欢迎批评指正

新浪简介 | About Sina | 广告服务 | 联系我们 | 招聘信息 | 网站律师 | SINA English | 产品答疑

新浪公司 版权所有