Code covered by the BSD License  

Highlights from
SAC_Sun2PC_mat

from SAC_Sun2PC_mat by Christos Saragiotis
This is utility to read SAC (Seismic Analysis Code) binary files created on/for SUN platform.

sac_sun2pc_mat(filename)
function sac_sun2pc_mat(filename)
% function sac_sun2pc_mat('filename')
%
% reads the SAC binary files from the SUN platform and creates a .mat file
%    that consists of 
%    1. a structure, H, that contains all the header information and 
%    2. a vector, X, that contains the waveform.
%
% The input file 'filename' must be either a sac file with extension .sac
%   or a path. In the latter case all sac files in this path are converted.
% The path must be the full path, i.e., 'C:\Data\920611_001538_00_405'. 
%   The mat files are saved in the folder, in which the original .sac file
%   exists.
%
% The SAC format files and names of variables are based on documentation, I
%   have found in the internet. Some SAC Header variables that are 'not 
%   currently used' (according to the documentations, I found) are in comments, 
%   whereas others are ommitted.
%
% This file has been tested (not thoroughly, though) using Matlab 6.5 but should 
%   be working on all other previous Matlab 6 or 5 versions as well.
% 
% Note that the file is NOT guaranteed to work perfectly, so please check
%   your results.
%
% Please send any comments or corrections to csar@auth.gr
% 
% SAC_SUN2PC_MAT written by C. D. Saragiotis on August 14th, 2003


F = 4-1; % float byte-size minus 1;
K = 8-1; % alphanumeric byte-size minus 1
L = 4-1; % long integer byte-size minus 1;

if isdir(filename)
    dirName = filename;
    dirContents = dir(filename);
    items = size(dirContents,1);
    eval(['cd ' filename])
    for j=1:items
        if length(dirContents(j).name)>4
            if sum(lower(dirContents(j).name(length(dirContents(j).name)-3:length(dirContents(j).name)))=='.sac')==4
                disp(dirContents(j).name)
                sourceFileId = fopen(dirContents(j).name,'rb');
                [X,H] = readSacFile(sourceFileId,F,K,L);
                st = fclose(sourceFileId);
                eval(['save ' dirContents(j).name(1:length(dirContents(j).name)-4) ' X H']);
                clear X,H;
            end
        end
    end
else 
    if sum(lower(filename(length(filename)-3:length(filename)))=='.sac')==4
            sourceFileId = fopen(filename,'rb');
            [X,H] = readSacFile(sourceFileId,F,K,L);
            st = fclose(sourceFileId);
            eval(['save ' filename(1:length(filename)-4) ' X H']);
    else
        error('The extension of the filename must be ''.sac'' or ''.SAC''. Try again...')
    end
end

function [data,header] = readSacFile(fid,F,K,L)

header = readSacHeader(fid,F,K,L);
npts = header(1).NPTS;
data = readSacData(fid,npts,F+1)';
   


function hdr = readSacHeader(fileId,F,K,L)
% hdr = readSacHeader(FID)
% sacReadAlphaNum reads the SAC-format header fields and returns most of them. 
%    It doesnot return the UNUSED and INTERNAL fields and those fields 
%    'not currently' according to documentation. Most of the fields not returned are 
%    placed in comments.
%    
%    The output variable, 'hdr' is a structure, whose fields are the fields
%    of the standard SAC header. For example hdr(1).NPTS returns the number
%    of points per data component.
%    
%    Created by C. D. Saragiotis, August 5th, 2003
headerBytes = 632;
chrctr = fread(fileId,headerBytes,'uchar');
chrctr = chrctr(:)';
hdr = struct([]);
% SAC header defaults
%INTERNAL4 = 6;
%INTERNAL5 = 0;
%INTERNAL6 = 0;
%UNUSED27 = 'FALSE';

% Read floats
hdr(1).DELTA  = sacReadFloat(chrctr(1:1+F)); % increment between evenly spaced samples
hdr(1).DEPMIN = sacReadFloat(chrctr(5:5+F)); % MINimum value of DEPendent variable
hdr(1).DEPMAX = sacReadFloat(chrctr(9:9+F)); % MAXimum value of DEPendent variable
% (not currently used) SCALE  = sacReadFloat(chrctr(13:13+F)); % Mult SCALE factor for dependent variable
hdr(1).ODELTA = sacReadFloat(chrctr(17:17+F)); % Observd increment if different than DELTA
hdr(1).B      = sacReadFloat(chrctr(21:21+F)); % Begining value of the independent variable
hdr(1).E      = sacReadFloat(chrctr(25:25+F)); % Ending value of the independent variable
hdr(1).O      = sacReadFloat(chrctr(29:29+F)); % event Origin time
hdr(1).A      = sacReadFloat(chrctr(33:33+F)); % first Arrival time

% Tn: user-defined Time-picks or markers n=1,2,...,9
% T(1)=T0, T(2)=T1, etc

% x = vec2mat(chrctr,F+1);
% X = sacReadFloat(x); 

hdr(1).T0 = sacReadFloat(chrctr(41:41+F)); hdr(1).T1 = sacReadFloat(chrctr(45:45+F)); 
hdr(1).T2 = sacReadFloat(chrctr(49:49+F)); hdr(1).T3 = sacReadFloat(chrctr(53:53+F)); 
hdr(1).T4 = sacReadFloat(chrctr(57:57+F)); hdr(1).T5 = sacReadFloat(chrctr(61:61+F)); 
hdr(1).T6 = sacReadFloat(chrctr(65:65+F)); hdr(1).T7 = sacReadFloat(chrctr(69:69+F)); 
hdr(1).T8 = sacReadFloat(chrctr(73:73+F)); hdr(1).T9 = sacReadFloat(chrctr(77:77+F)); 

hdr(1).F      = sacReadFloat(chrctr(81:81+F)); % Fini of event time

% RESPn: instrument RESPonse parameters n=1,2,...,9
% RESP(1)=RESP0, RESP(2)=RESP1, etc
% (not currently used) i = 85:F+1:121; RESP(1:10,1) = sacReadFloat(chrctr(i:i+F)); 

hdr(1).STLA   = sacReadFloat(chrctr(125:125+F)); % STation LAttitude
hdr(1).STLO   = sacReadFloat(chrctr(129:129+F)); % STation LOngtitude
hdr(1).STEL   = sacReadFloat(chrctr(133:133+F)); % STation ELevation
% (not currently used) STDP   = sacReadFloat(chrctr(137:137+F)); % STation DePth below surface 

hdr(1).EVLA   = sacReadFloat(chrctr(141:141+F)); % EVent LAttitude
hdr(1).EVLO   = sacReadFloat(chrctr(145:145+F)); % EVent LOngtitude
% (not currently used) EVEL   = sacReadFloat(chrctr(149:149+F)); % EVent ELevation
hdr(1).EVDP   = sacReadFloat(chrctr(153:153+F)); % EVent DePth below surface

% USERn: USER-defined-variable storage area, n=1,2,...,9
% user(1)=USER0, user(2)=USER1, etc
hdr(1).USER0 = sacReadFloat(chrctr(161:161+F)); hdr(1).USER1 = sacReadFloat(chrctr(165:165+F)); 
hdr(1).USER2 = sacReadFloat(chrctr(169:169+F)); hdr(1).USER3 = sacReadFloat(chrctr(173:173+F)); 
hdr(1).USER4 = sacReadFloat(chrctr(177:177+F)); hdr(1).USER5 = sacReadFloat(chrctr(181:181+F)); 
hdr(1).USER6 = sacReadFloat(chrctr(185:185+F)); hdr(1).USER7 = sacReadFloat(chrctr(189:189+F)); 
hdr(1).USER8 = sacReadFloat(chrctr(193:193+F)); hdr(1).USER9 = sacReadFloat(chrctr(197:197+F)); 

hdr(1).DIST   = sacReadFloat(chrctr(201:201+F)); % station to event DISTance (km)
hdr(1).AZ     = sacReadFloat(chrctr(205:205+F)); % event to station AZimuth (degrees)
hdr(1).BAZ    = sacReadFloat(chrctr(209:209+F)); % station to event AZimuth (degrees)
hdr(1).GCARC  = sacReadFloat(chrctr(213:213+F)); % station to event Great Circle ARC length (degrees)

hdr(1).DEPMEN = sacReadFloat(chrctr(225:225+F)); % MEaN value of DEPendent variable
hdr(1).CMPAZ  = sacReadFloat(chrctr(229:229+F)); % CoMPonent AZimuth (degrees clockwise from north)
hdr(1).CMPINC = sacReadFloat(chrctr(233:233+F)); % CoMPonent INCident angle (degrees from vertical)

% Read long integers
hdr(1).NZYEAR = sacReadLong(chrctr(281:281+L)); % GMT YEAR
hdr(1).NZJDAY = sacReadLong(chrctr(285:285+L)); % GMT julian DAY
hdr(1).NZHOUR = sacReadLong(chrctr(289:289+L)); % GMT HOUR
hdr(1).NZMIN  = sacReadLong(chrctr(293:293+L)); % GMT MINute
hdr(1).NZSEC  = sacReadLong(chrctr(297:297+L)); % GMT SECond
hdr(1).NZMSEC = sacReadLong(chrctr(301:301+L)); % GMT MilliSECond

hdr(1).NPTS   = sacReadLong(chrctr(317:317+L)); % Number of PoinTS per data component

% Some SAC defaults
hdr(1).IFTYPE = 'TIME SERIES FILE'; % File TYPE
%IFTYPE  = sacReadLong(chrctr(341:341+L)) % File TYPE
hdr(1).IDEP = 'UNKNOWN'; % type of DEPendent variable
%IDEP    = sacReadLong(chrctr(345:345+L)) % type of DEPendent variable
hdr(1).IZTYPE = 'BEGIN FILE (B)'; % reference time equivalence
%IZTYPE  = sacReadLong(chrctr(349:349+L)) % reference time equivalence

% (not currently used) IINST  = sacReadLong(chrctr(357:357+L)) % type of recording INSTrument

%% Again read floats
% (not currently used) ISTREG = sacReadFloat(chrctr(361:361+F)); % STation geographic REGion
% (not currently used) IEVREG = sacReadFloat(chrctr(365:365+F)); % EVent geographic REGion
% (not currently used) IQUAL  = sacReadFloat(chrctr(373:373+F)); % QUALity of data
% (not currently used) ISYNTH = sacReadFloat(chrctr(377:377+F)); % SYNTHetic data flag

% SAC defaults
hdr(1).IEVTYPE = 'IUNKN';
%IEVTYPE= sacReadFloat(chrctr(369:369+F)); % EVent TYPE
hdr(1).LEVEN = 'TRUE';  % true, if data are EVENly spaced (required)
%LEVEN= sacReadFloat(chrctr(421:521+F)); % true, if data are EVENly spaced (required)
hdr(1).LPSPOL = 'FALSE'; % true, if station components have a PoSitive POLarity
%LPSPOL= sacReadFloat(chrctr(425:425+F)); % true, if station components have a PoSitive POLarity
hdr(1).LOVROK = 'FALSE'; % true, if it is OK to OVeRwrite this file in disk
%LOVROK= sacReadFloat(chrctr(429:429+F)); % true, if it is OK to OVeRwrite this file in disk
hdr(1).LCALDA = 'TRUE'; % true, if DIST, AZ, BAZ and GCARC are to be calculated from station and event coordinates
%LCALDA= sacReadFloat(chrctr(433:433+F)); % true, if DIST, AZ, BAZ and GCARC are to be calculated from station and event coordinates

% Read alphanumeric data
hdr(1).KSTNM  = sacReadAlphaNum(chrctr(441:441+K)); % STation NaMe
hdr(1).KEVNM  = sacReadAlphaNum(chrctr(449:449+2*(K+1)-1)); % EVent NaMe
hdr(1).KHOLE  = sacReadAlphaNum(chrctr(465:465+K)); % HOLE identification, if nuclear event
hdr(1).KO     = sacReadAlphaNum(chrctr(473:473+K)); % event Origin time identification
hdr(1).KA     = sacReadAlphaNum(chrctr(481:481+K)); % first Arrival time identification
% 
% % KTn: user-defined Time pick identifications, n=1,2,...,9
% % KT0=KT(1), KT1=kt(2), etc
% i = 489:F+1:561; kt(1:10,1) = sacReadFloat(chrctr(i:i+K)); 
% hdr(1).KT0 = kt(1); hdr(1).KT1 = kt(2); hdr(1).KT2 = kt(3); hdr(1).KT3 = kt(4); hdr(1).KT4 = kt(5); 
% hdr(1).KT5 = kt(6); hdr(1).KT6 = kt(7); hdr(1).KT7 = kt(8); hdr(1).KT8 =
% kt(9); hdr(1).KT9 = kt(10); 

hdr(1).KF     = sacReadAlphaNum(chrctr(569:569+K)); % Fini identification
hdr(1).KUSER0 = sacReadAlphaNum(chrctr(577:577+K)); % USER-defined variable storage area
hdr(1).KUSER1 = sacReadAlphaNum(chrctr(585:585+K)); % USER-defined variable storage area
hdr(1).KUSER2 = sacReadAlphaNum(chrctr(593:593+K)); % USER-defined variable storage area
hdr(1).KCMPNM = sacReadAlphaNum(chrctr(601:601+K)); % CoMPonent NaMe
hdr(1).KNETWK = sacReadAlphaNum(chrctr(609:609+K)); % name of seismic NETWorK
hdr(1).KDATRD = sacReadAlphaNum(chrctr(617:617+K)); % DATa Recording Date onto the computer
hdr(1).KINST  = sacReadAlphaNum(chrctr(625:625+K)); % generic name of recording INSTrument


function X = readSacData(fid,N,F)
% function data = readSacData('filename',NPTS,floatByteSize)
headerBytes = 632;
% Syntax: [A, COUNT] = FREAD(FID,SIZE,PRECISION)
chrctr = fread(fid,N*F,'uchar');
x = vec2mat(chrctr,F);
X = sacReadFloat(x); 
%    if rem(i,1000)==0, disp(i),end


function lNumber = sacReadLong(cb)
% reads long integers (4 bytes long)
% cb is the character buffer
cb = cb(:);
lNumber = (256.^(3:-1:0))*cb;
if lNumber == -12345, lNumber = []; end

function fNumber = sacReadFloat(cb)
% reads floats (4 bytes long)
% cb is the character buffer
C = size(cb,1);
stringOfBitSequence = [dec2bin(cb(:,1),8) dec2bin(cb(:,2),8) dec2bin(cb(:,3),8) dec2bin(cb(:,4),8)];
bitSequence = stringOfBitSequence=='1';
fSign = -2*bitSequence(:,1)+1;
fExp = bitSequence(:,2:9)*(2.^(7:-1:0)') - 127;
fMantissa = [ones(C,1) bitSequence(:,10:32)]*(2.^(0:-1:-23)');
fNumber = fSign.*fMantissa.*(2.^fExp);
isZeroCheck = sum(bitSequence')'==0;
fNumber = fNumber.*(~isZeroCheck);
if fNumber == -12345, fNumber = ' '; end


function alphaNum = sacReadAlphaNum(cb)
% reads alphanumeric data (8 or 16 bytes long). If it cb is empty, it returns a ' ' 
% cb is the character buffer
K = max(size(cb));
alphaNumTemp = char(cb);
if K == 8
    if alphaNumTemp == '-12345  ' 
        alphaNum = ' ';
    else
        alphaNum = alphaNumTemp;
    end
else
    if K == 16
        if alphaNumTemp == '-12345   -12345 ' 
            alphaNum = ' ';
        else
            alphaNum = alphaNumTemp;
        end
    end
end

Contact us at files@mathworks.com