.
sac2mseed-1.12+ds1/Makefile 0000664 0000000 0000000 00000000577 13225712270 0015440 0 ustar 00root root 0000000 0000000
DIRS = libmseed src
all clean static install gcc gcc32 gcc64 debug gccdebug gcc32debug gcc64debug ::
@for d in $(DIRS) ; do \
echo "Running $(MAKE) $@ in $$d" ; \
if [ -f $$d/Makefile -o -f $$d/makefile ] ; \
then ( cd $$d && $(MAKE) $@ ) ; \
elif [ -d $$d ] ; \
then ( echo "ERROR: no Makefile/makefile in $$d for $(CC)" ) ; \
fi ; \
done
sac2mseed-1.12+ds1/Makefile.wat 0000664 0000000 0000000 00000000562 13225712270 0016224 0 ustar 00root root 0000000 0000000 #
# THIS FILE IS DEPRECATED AND WILL BE REMOVED IN A FUTURE RELEASE
#
# Wmake File for seisan2mseed - For Watcom's wmake
# Use 'wmake -f Makefile.wat'
all: .SYMBOLIC
cd libmseed
wmake -f Makefile.wat
cd ..\src
wmake -f Makefile.wat
cd ..
clean: .SYMBOLIC
cd libmseed
wmake -f Makefile.wat clean
cd ..\src
wmake -f Makefile.wat clean
cd ..
sac2mseed-1.12+ds1/Makefile.win 0000664 0000000 0000000 00000000417 13225712270 0016225 0 ustar 00root root 0000000 0000000 #
# Nmake file - For MS Visual C++ version
# Use 'nmake -f Makefile.win'
all:
cd libmseed
nmake -f Makefile.win
cd ..\src
nmake -f Makefile.win
cd ..
clean:
cd libmseed
nmake -f Makefile.win clean
cd ..\src
nmake -f Makefile.win clean
cd ..
sac2mseed-1.12+ds1/README.md 0000664 0000000 0000000 00000001170 13225712270 0015245 0 ustar 00root root 0000000 0000000 sac2mseed - Convert SAC waveform data to Mini-SEED.
## Documentation
For usage infromation see the [sac2mseed manual](doc/sac2mseed.md) in the
'doc' directory.
## Downloading and building
The [releases](https://github.com/iris-edu/sac2mseed/releases) area
contains release versions.
In most Unix/Linux environments a simple 'make' will build the program.
The CC and CFLAGS environment variables can be used to configure
the build parameters.
In the Win32 environment the Makefile.win can be used with the nmake
build tool included with Visual Studio.
## Licensing
GNU GPL version 3. See included LICENSE file for details.
sac2mseed-1.12+ds1/doc/ 0000775 0000000 0000000 00000000000 13225712270 0014534 5 ustar 00root root 0000000 0000000 sac2mseed-1.12+ds1/doc/sac2mseed.1 0000664 0000000 0000000 00000013514 13225712270 0016470 0 ustar 00root root 0000000 0000000 .TH SAC2MSEED 1 2017/04/03
.SH NAME
SAC to miniSEED converter
.SH SYNOPSIS
.nf
sac2mseed [options] file1 [file2 file3 ...]
.fi
.SH DESCRIPTION
\fBsac2mseed\fP converts SAC waveform data to miniSEED format. By
default the format of the input files is automatically detected: alpha
or binary (byte order autodetected). The format can also be forced
with the \fI-f\fP option. If an input file name is prefixed with an '@'
character the file is assumed to contain a list of input data files,
see \fILIST FILES\fP below.
If the input file name ends in ".sac" (not case sensitive) the default
output file name will be the same with the extension replace with
".mseed". The output data may be re-directed to a single file or
stdout using the -o option.
.SH OPTIONS
.IP "-V "
Print program version and exit.
.IP "-h "
Print program usage and exit.
.IP "-v "
Be more verbose. This flag can be used multiple times ("-v -v" or
"-vv") for more verbosity.
.IP "-S "
Include SEED blockette 100 in each output record with the sample rate
in floating point format. The basic format for storing sample rates
in SEED data records is a rational approximation
(numerator/denominator). Precision will be lost if a given sample
rate cannot be well approximated. This option should be used in those
cases.
.IP "-n \fInetcode\fP"
Specify the SEED network code to use, if not specified the network
code will be the value of the KNETWK variable in the SAC header, if
KNETWK is not specified the network code will be blank. It is highly
recommended to specify a network code if no network is defined in the
SAC file.
.IP "-s \fIstacode\fP"
Specify the SEED station code to use, if not specified the station
code will be the value of the KSTNM variable in the SAC header, if
KSTNM is not specified the location ID will be blank.
.IP "-l \fIlocid\fP"
Specify the SEED location ID to use, if not specified the location
ID will be the value of the KHOLE variable in the SAC header, if KHOLE
is not specified the location ID will be blank.
.IP "-c \fIchancodes\fP"
Specify the SEED channel codes to use, if not specified the channel
code will be the value of the KCMPNM variable in the SAC header, if
KCMPNM is not specified the location ID will be blank. As a special
case a dot (.) will be interpreted as the same character as the input
channel name, for example, "L.." can be specified to only replace the
first code with 'L' and leave the other two codes as they are.
.IP "-r \fIbytes\fP"
Specify the miniSEED record length in \fIbytes\fP, default is 4096.
.IP "-e \fIencoding\fP"
Specify the miniSEED data encoding format, default is 11 (Steim-2
compression). Other supported encoding formats include 10 (Steim-1
compression), 1 (16-bit integers) and 3 (32-bit integers). The 16-bit
integers encoding should only be used if all data samples can be
represented in 16 bits.
.IP "-b \fIbyteorder\fP"
Specify the miniSEED byte order, default is 1 (big-endian or most
significant byte first). The other option is 0 (little-endian or
least significant byte first). It is highly recommended to always
create big-endian SEED.
.IP "-o \fIoutfile\fP"
Write all miniSEED records to \fIoutfile\fP, if \fIoutfile\fP is a
single dash (-) then all miniSEED output will go to stdout. All
diagnostic output from the program is written to stderr and should
never get mixed with data going to stdout.
.IP "-m \fImetafile\fP"
For each input SAC file write a one-line summary of channel metadata
\fImetafile\fP. The one-line summary is a comma-separated list
containing: network, station, location, channel, latitude, longitude,
elevation, depth, azimuth, incidence, instrument name, scale factor,
sampling rate and start and end times. In SAC the component azimuth
is in degrees clockwise from north, the component incident angle is in
degrees from vertical and the elevation and depth are both in meters.
.IP "-me "
When writing out a metadata file include the event name (kevnm) and
user strings 0, 1 and 2 (kuser0, kuser1 and kuser2).
.IP "-s \fIfactor\fP"
When writing data to an integer (miniSEED) encoding format apply this
scaling \fIfactor\fP to each input floating point data sample before
truncating to an integer. By default autoscaling is used and a
scaling factor is determined that will scale the maximum sample value
to a minimum of 6 digits. If none of the input sample values include
fractional components the scaling factor will be 1 and the floating
point data will simply be truncated to their integer components.
.IP "-f \fIformat\fP"
By default the format of each input file is autodetected, either alpha
or binary (little or big endian byte order autodetected as well).
This option forces the format for every input file:
.nf
0 : Autodetect SAC format (default)
1 : Alphanumeric SAC format
2 : Binary SAC format, autodetect byte order
3 : Binary SAC format, little-endian
4 : Binary SAC format, big-endian
.fi
.SH SEED LOCATION IDS
The contents of the SAC header variable KHOLE is used as the SEED
location ID if it is set. While the definition of KHOLE and SEED
location ID are not officially the same, this is a known convention
when converting between these two formats.
.SH LIST FILES
If an input file is prefixed with an '@' character the file is assumed
to contain a list of file for input. Multiple list files can be
combined with multiple input files on the command line. The last,
space separated field on each line is assumed to be the file name to
be read.
An example of a simple text list:
.nf
TA.ELFS..LHE.SAC
TA.ELFS..LHN.SAC
TA.ELFS..LHZ.SAC
.fi
.SH ABOUT SAC
Seismic Analysis Code (SAC) is a general purpose interactive program
designed for the study of sequential signals, especially timeseries
data. Originally developed at the Lawrence Livermore National
Laboratory the SAC software package is also available from IRIS.
.SH AUTHOR
.nf
Chad Trabant
IRIS Data Management Center
.fi
sac2mseed-1.12+ds1/doc/sac2mseed.md 0000664 0000000 0000000 00000015527 13225712270 0016736 0 ustar 00root root 0000000 0000000 # SAC to miniSEED converter
1. [Name](#)
1. [Synopsis](#synopsis)
1. [Description](#description)
1. [Options](#options)
1. [Seed Location Ids](#seed-location-ids)
1. [List Files](#list-files)
1. [About Sac](#about-sac)
1. [Author](#author)
## Synopsis
sac2mseed [options] file1 [file2 file3 ...]
## Description
sac2mseed converts SAC waveform data to miniSEED format. By default the format of the input files is automatically detected: alpha or binary (byte order autodetected). The format can also be forced with the -f option. If an input file name is prefixed with an '@' character the file is assumed to contain a list of input data files, see LIST FILES below.
If the input file name ends in ".sac" (not case sensitive) the default output file name will be the same with the extension replace with ".mseed". The output data may be re-directed to a single file or stdout using the -o option.
## Options
-V
Print program version and exit.
-h
Print program usage and exit.
-v
Be more verbose. This flag can be used multiple times ("-v -v" or "-vv") for more verbosity.
-S
Include SEED blockette 100 in each output record with the sample rate in floating point format. The basic format for storing sample rates in SEED data records is a rational approximation (numerator/denominator). Precision will be lost if a given sample rate cannot be well approximated. This option should be used in those cases.
-n netcode
Specify the SEED network code to use, if not specified the network code will be the value of the KNETWK variable in the SAC header, if KNETWK is not specified the network code will be blank. It is highly recommended to specify a network code if no network is defined in the SAC file.
-s stacode
Specify the SEED station code to use, if not specified the station code will be the value of the KSTNM variable in the SAC header, if KSTNM is not specified the location ID will be blank.
-l locid
Specify the SEED location ID to use, if not specified the location ID will be the value of the KHOLE variable in the SAC header, if KHOLE is not specified the location ID will be blank.
-c chancodes
Specify the SEED channel codes to use, if not specified the channel code will be the value of the KCMPNM variable in the SAC header, if KCMPNM is not specified the location ID will be blank. As a special case a dot (.) will be interpreted as the same character as the input channel name, for example, "L.." can be specified to only replace the first code with 'L' and leave the other two codes as they are.
-r bytes
Specify the miniSEED record length in bytes, default is 4096.
-e encoding
Specify the miniSEED data encoding format, default is 11 (Steim-2 compression). Other supported encoding formats include 10 (Steim-1 compression), 1 (16-bit integers) and 3 (32-bit integers). The 16-bit integers encoding should only be used if all data samples can be represented in 16 bits.
-b byteorder
Specify the miniSEED byte order, default is 1 (big-endian or most significant byte first). The other option is 0 (little-endian or least significant byte first). It is highly recommended to always create big-endian SEED.
-o outfile
Write all miniSEED records to outfile, if outfile is a single dash (-) then all miniSEED output will go to stdout. All diagnostic output from the program is written to stderr and should never get mixed with data going to stdout.
-m metafile
For each input SAC file write a one-line summary of channel metadata metafile. The one-line summary is a comma-separated list containing: network, station, location, channel, latitude, longitude, elevation, depth, azimuth, incidence, instrument name, scale factor, sampling rate and start and end times. In SAC the component azimuth is in degrees clockwise from north, the component incident angle is in degrees from vertical and the elevation and depth are both in meters.
-me
When writing out a metadata file include the event name (kevnm) and user strings 0, 1 and 2 (kuser0, kuser1 and kuser2).
-s factor
When writing data to an integer (miniSEED) encoding format apply this scaling factor to each input floating point data sample before truncating to an integer. By default autoscaling is used and a scaling factor is determined that will scale the maximum sample value to a minimum of 6 digits. If none of the input sample values include fractional components the scaling factor will be 1 and the floating point data will simply be truncated to their integer components.
-f format
By default the format of each input file is autodetected, either alpha or binary (little or big endian byte order autodetected as well). This option forces the format for every input file:
0 : Autodetect SAC format (default)
1 : Alphanumeric SAC format
2 : Binary SAC format, autodetect byte order
3 : Binary SAC format, little-endian
4 : Binary SAC format, big-endian
## Seed Location Ids
The contents of the SAC header variable KHOLE is used as the SEED location ID if it is set. While the definition of KHOLE and SEED location ID are not officially the same, this is a known convention when converting between these two formats.
## List Files
If an input file is prefixed with an '@' character the file is assumed to contain a list of file for input. Multiple list files can be combined with multiple input files on the command line. The last, space separated field on each line is assumed to be the file name to be read.
An example of a simple text list:
TA.ELFS..LHE.SAC
TA.ELFS..LHN.SAC
TA.ELFS..LHZ.SAC
## About Sac
Seismic Analysis Code (SAC) is a general purpose interactive program designed for the study of sequential signals, especially timeseries data. Originally developed at the Lawrence Livermore National Laboratory the SAC software package is also available from IRIS.
## Author
Chad Trabant
IRIS Data Management Center
(man page 2017/04/03)
sac2mseed-1.12+ds1/src/ 0000775 0000000 0000000 00000000000 13225712270 0014556 5 ustar 00root root 0000000 0000000 sac2mseed-1.12+ds1/src/Makefile 0000664 0000000 0000000 00000002171 13225712270 0016217 0 ustar 00root root 0000000 0000000
# Build environment can be configured the following
# environment variables:
# CC : Specify the C compiler to use
# CFLAGS : Specify compiler options to use
# Options specific for GCC
GCC = gcc
GCCFLAGS = -O2 -Wall -I../libmseed
# Required compiler parameters
REQCFLAGS = -I../libmseed
BIN = sac2mseed
LDFLAGS = -L../libmseed
LDLIBS = -lmseed
OBJS = $(BIN).o
all: $(BIN)
$(BIN): $(OBJS)
$(CC) $(CFLAGS) -o ../$@ $(OBJS) $(LDFLAGS) $(LDLIBS)
clean:
rm -f $(OBJS) ../$(BIN)
cc:
@$(MAKE) "CC=$(CC)" "CFLAGS=$(CFLAGS)"
gcc:
@$(MAKE) "CC=$(GCC)" "CFLAGS=$(GCCFLAGS)"
gcc32:
@$(MAKE) "CC=$(GCC)" "CFLAGS=-m32 $(GCCFLAGS)"
gcc64:
@$(MAKE) "CC=$(GCC)" "CFLAGS=-m64 $(GCCFLAGS)"
static:
@$(MAKE) "CC=$(CC)" "CFLAGS=-static $(GCCFLAGS)"
debug:
$(MAKE) "CFLAGS=-g $(CFLAGS)"
gccdebug:
$(MAKE) "CC=$(GCC)" "CFLAGS=-g $(GCCFLAGS)"
gcc32debug:
$(MAKE) "CC=$(GCC)" "CFLAGS=-g -m32 $(GCCFLAGS)"
gcc64debug:
$(MAKE) "CC=$(GCC)" "CFLAGS=-g -m64 $(GCCFLAGS)"
# Implicit rule for building object files
%.o: %.c
$(CC) $(CFLAGS) $(REQCFLAGS) -c $<
install:
@echo
@echo "No install target, copy the executable(s) yourself"
@echo
sac2mseed-1.12+ds1/src/Makefile.wat 0000664 0000000 0000000 00000001274 13225712270 0017014 0 ustar 00root root 0000000 0000000 #
# THIS FILE IS DEPRECATED AND WILL BE REMOVED IN A FUTURE RELEASE
#
# Wmake File - for Watcom's wmake
# Use 'wmake -f Makefile.wat'
.BEFORE
@set INCLUDE=.;$(%watcom)\H;$(%watcom)\H\NT
@set LIB=.;$(%watcom)\LIB386
cc = wcc386
cflags = -zq
lflags = OPT quiet OPT map LIBRARY ..\libmseed\libmseed.lib
cvars = $+$(cvars)$- -DWIN32
BIN = ..\sac2mseed.exe
INCS = -I..\libmseed
all: $(BIN)
$(BIN): sac2mseed.obj
wlink $(lflags) name $(BIN) file {sac2mseed.obj}
# Source dependencies:
sac2mseed.obj: sac2mseed.c
# How to compile sources:
.c.obj:
$(cc) $(cflags) $(cvars) $(INCS) $[@ -fo=$@
# Clean-up directives:
clean: .SYMBOLIC
del *.obj *.map $(BIN)
sac2mseed-1.12+ds1/src/Makefile.win 0000664 0000000 0000000 00000000631 13225712270 0017012 0 ustar 00root root 0000000 0000000 #
# Nmake file - Windows version
# Use 'nmake -f Makefile.win'
NODEBUG=1
INCS = /I..\libmseed
OPTS = -D_CRT_SECURE_NO_WARNINGS -DNOFDZIP
LIBS = ..\libmseed\libmseed.lib
BIN = ..\sac2mseed.exe
all: $(BIN)
$(BIN): sac2mseed.obj
link.exe /nologo /out:$(BIN) $(LIBS) sac2mseed.obj
.c.obj:
$(CC) /nologo $(CFLAGS) $(INCS) $(OPTS) /c $<
# Clean-up directives
clean:
-del a.out core *.o *.obj *% *~ $(BIN)
sac2mseed-1.12+ds1/src/sac2mseed.c 0000664 0000000 0000000 00000117171 13225712270 0016600 0 ustar 00root root 0000000 0000000 /***************************************************************************
* sac2mseed.c
*
* Simple waveform data conversion from SAC timeseries to Mini-SEED.
* No support is included for SAC spectral or generic X-Y data.
*
* Written by Chad Trabant, IRIS Data Management Center
*
* modified 2013.288
***************************************************************************/
#include
#include
#include
#include
#include
#include
#include
#include "sacformat.h"
#define VERSION "1.12"
#define PACKAGE "sac2mseed"
#if defined (LWP_WIN)
#define strtoull _strtoui64
#endif
struct listnode {
char *key;
char *data;
struct listnode *next;
};
static void packtraces (flag flush);
static int sac2group (char *sacfile, MSTraceGroup *mstg);
static int parsesac (FILE *ifp, struct SACHeader *sh, float **data, int format,
int verbose, char *sacfile);
static int readbinaryheader (FILE *ifp, struct SACHeader *sh, int *format,
int *swapflag, int verbose, char *sacfile);
static int readbinarydata (FILE *ifp, float *data, int datacnt,
int swapflag, int verbose, char *sacfile);
static int readalphaheader (FILE *ifp, struct SACHeader *sh);
static int readalphadata (FILE *ifp, float *data, int datacnt);
static int swapsacheader (struct SACHeader *sh);
static int writemetadata (struct SACHeader *sh, char *network, char *station,
char *location, char *channel, hptime_t starttime,
int expanded);
static int parameter_proc (int argcount, char **argvec);
static char *getoptval (int argcount, char **argvec, int argopt);
static int readlistfile (char *listfile);
static void addnode (struct listnode **listroot, char *key, char *data);
static void record_handler (char *record, int reclen, void *handlerdata);
static void usage (void);
static int verbose = 0;
static int packreclen = -1;
static int encoding = 11;
static int byteorder = -1;
static int sacformat = 0;
static int expmeta = 0;
static char srateblkt = 0;
static char *forcenet = 0;
static char *forcesta = 0;
static char *forceloc = 0;
static char *forcechan = 0;
static char *outputfile = 0;
static FILE *ofp = 0;
static char *metafile = 0;
static FILE *mfp = 0;
static long long int datascaling = 0;
/* A list of input files */
struct listnode *filelist = 0;
static MSTraceGroup *mstg = 0;
static int packedtraces = 0;
static int64_t packedsamples = 0;
static int64_t packedrecords = 0;
int
main (int argc, char **argv)
{
struct listnode *flp;
/* Process given parameters (command line and parameter file) */
if (parameter_proc (argc, argv) < 0)
return -1;
/* Init MSTraceGroup */
mstg = mst_initgroup (mstg);
/* Open the output file if specified */
if ( outputfile )
{
if ( strcmp (outputfile, "-") == 0 )
{
ofp = stdout;
}
else if ( (ofp = fopen (outputfile, "wb")) == NULL )
{
fprintf (stderr, "Cannot open output file: %s (%s)\n",
outputfile, strerror(errno));
return -1;
}
}
/* Open the metadata output file if specified */
if ( metafile )
{
if ( strcmp (metafile, "-") == 0 )
{
mfp = stdout;
}
else if ( (mfp = fopen (metafile, "wb")) == NULL )
{
fprintf (stderr, "Cannot open metadata output file: %s (%s)\n",
metafile, strerror(errno));
return -1;
}
}
/* Read input SAC files into MSTraceGroup */
flp = filelist;
while ( flp != 0 )
{
if ( verbose )
fprintf (stderr, "Reading %s\n", flp->data);
sac2group (flp->data, mstg);
flp = flp->next;
}
fprintf (stderr, "Packed %d trace(s) of %lld samples into %lld records\n",
packedtraces, (long long int)packedsamples, (long long int)packedrecords);
/* Make sure everything is cleaned up */
if ( ofp )
fclose (ofp);
if ( mfp )
fclose (mfp);
return 0;
} /* End of main() */
/***************************************************************************
* packtraces:
*
* Pack all traces in a group using per-MSTrace templates.
*
* Returns 0 on success, and -1 on failure
***************************************************************************/
static void
packtraces (flag flush)
{
MSTrace *mst;
int64_t trpackedsamples = 0;
int64_t trpackedrecords = 0;
mst = mstg->traces;
while ( mst )
{
if ( mst->numsamples <= 0 )
{
mst = mst->next;
continue;
}
trpackedrecords = mst_pack (mst, &record_handler, 0, packreclen, encoding, byteorder,
&trpackedsamples, flush, verbose-2, (MSRecord *) mst->prvtptr);
if ( trpackedrecords < 0 )
{
fprintf (stderr, "Error packing data\n");
}
else
{
packedrecords += trpackedrecords;
packedsamples += trpackedsamples;
}
mst = mst->next;
}
} /* End of packtraces() */
/***************************************************************************
* sac2group:
* Read a SAC file and add data samples to a MSTraceGroup. As the SAC
* data is read in a MSRecord struct is used as a holder for the input
* information.
*
* Returns 0 on success, and -1 on failure
***************************************************************************/
static int
sac2group (char *sacfile, MSTraceGroup *mstg)
{
FILE *ifp = 0;
MSRecord *msr = 0;
MSTrace *mst;
struct blkt_1000_s Blkt1000;
struct blkt_1001_s Blkt1001;
struct blkt_100_s Blkt100;
struct SACHeader sh;
float *fdata = 0;
int32_t *idata = 0;
int dataidx;
int datacnt;
long long int scaling = datascaling;
/* Open input file */
if ( (ifp = fopen (sacfile, "rb")) == NULL )
{
fprintf (stderr, "Cannot open input file: %s (%s)\n",
sacfile, strerror(errno));
return -1;
}
/* Parse input SAC file into a header structure and data buffer */
if ( (datacnt = parsesac (ifp, &sh, &fdata, sacformat, verbose, sacfile)) < 0 )
{
fprintf (stderr, "Error parsing %s\n", sacfile);
return -1;
}
/* Open output file if needed */
if ( ! ofp )
{
char mseedoutputfile[1024];
int namelen;
strncpy (mseedoutputfile, sacfile, sizeof(mseedoutputfile)-6 );
namelen = strlen (sacfile);
/* Truncate file name if .sac is at the end */
if ( namelen > 4 )
if ( (*(mseedoutputfile + namelen - 1) == 'c' || *(mseedoutputfile + namelen - 1) == 'C') &&
(*(mseedoutputfile + namelen - 2) == 'a' || *(mseedoutputfile + namelen - 2) == 'A') &&
(*(mseedoutputfile + namelen - 3) == 's' || *(mseedoutputfile + namelen - 3) == 'S') &&
(*(mseedoutputfile + namelen - 4) == '.') )
{
*(mseedoutputfile + namelen - 4) = '\0';
}
/* Add .mseed to the file name */
strcat (mseedoutputfile, ".mseed");
if ( (ofp = fopen (mseedoutputfile, "wb")) == NULL )
{
fprintf (stderr, "Cannot open output file: %s (%s)\n",
mseedoutputfile, strerror(errno));
return -1;
}
}
if ( ! (msr = msr_init(msr)) )
{
fprintf (stderr, "Cannot initialize MSRecord strcture\n");
return -1;
}
/* Determine autoscaling */
if ( scaling == 0 && encoding != 4 )
{
float datamin, datamax;
int fractional;
long long int autoscale;
/* Determine data sample minimum and maximum
* Detect if scaling by 1 will result in truncation (fractional=1) */
datamin = datamax = *fdata;
fractional = 0;
for ( dataidx=1; dataidx < datacnt; dataidx++ )
{
if ( *(fdata+dataidx) < datamin ) datamin = *(fdata+dataidx);
if ( *(fdata+dataidx) > datamax ) datamax = *(fdata+dataidx);
if ( ! fractional )
if ( *(fdata+dataidx) - (int) *(fdata+dataidx) > 0.000001 )
fractional = 1;
}
autoscale = 1;
if ( fractional )
{
for (autoscale=1; abs ((int32_t) (datamax * autoscale)) < 100000; autoscale *= 10) {}
if ( abs ((int32_t) (datamin * autoscale)) < 10 )
fprintf (stderr, "WARNING Large sample value range (%g/%g), autoscaling might be a bad idea\n",
datamax, datamin);
}
scaling = autoscale;
}
/* Populate MSRecord structure with header details */
if ( strncmp (SUNDEF, sh.knetwk, 8) ) ms_strncpclean (msr->network, sh.knetwk, 2);
if ( strncmp (SUNDEF, sh.kstnm, 8) ) ms_strncpclean (msr->station, sh.kstnm, 5);
if ( strncmp (SUNDEF, sh.khole, 8) ) ms_strncpclean (msr->location, sh.khole, 2);
if ( strncmp (SUNDEF, sh.kcmpnm, 8) ) ms_strncpclean (msr->channel, sh.kcmpnm, 3);
if ( forcenet )
ms_strncpclean (msr->network, forcenet, 2);
if ( forcesta )
ms_strncpclean (msr->station, forcesta, 5);
if ( forceloc )
ms_strncpclean (msr->location, forceloc, 2);
if ( forcechan )
{
int idx = 0;
while ( forcechan[idx] && idx < (sizeof(msr->channel)-1) )
{
if ( forcechan[idx] != '.' )
msr->channel[idx] = forcechan[idx];
idx++;
}
msr->channel[idx] = '\0';
}
msr->starttime = ms_time2hptime (sh.nzyear, sh.nzjday, sh.nzhour, sh.nzmin, sh.nzsec, sh.nzmsec * 1000);
/* Adjust for Begin ('B' SAC variable) time offset */
msr->starttime += (double) sh.b * HPTMODULUS;
/* Calculate sample rate from interval(period) rounding to nearest 0.000001 Hz */
msr->samprate = (double) ((int)((1 / sh.delta) * 100000 + 0.5)) / 100000;
msr->samplecnt = msr->numsamples = datacnt;
/* Data sample type and sample array */
if ( encoding == 4 )
{
msr->sampletype = 'f';
msr->datasamples = fdata;
}
else
{
/* Create an array of scaled integers */
idata = (int32_t *) malloc (datacnt * sizeof(int32_t));
if ( verbose )
fprintf (stderr, "[%s] Creating integer data scaled by: %lld\n", sacfile, scaling);
for ( dataidx=0; dataidx < datacnt; dataidx++ )
*(idata + dataidx) = (int32_t) (*(fdata + dataidx) * scaling);
msr->sampletype = 'i';
msr->datasamples = idata;
}
if ( verbose >= 1 )
{
fprintf (stderr, "[%s] %lld samps @ %.6f Hz for N: '%s', S: '%s', L: '%s', C: '%s'\n",
sacfile, (long long int)msr->numsamples, msr->samprate,
msr->network, msr->station, msr->location, msr->channel);
}
if ( ! (mst = mst_addmsrtogroup (mstg, msr, 0, -1.0, -1.0)) )
{
fprintf (stderr, "[%s] Error adding samples to MSTraceGroup\n", sacfile);
}
/* Create an MSRecord template for the MSTrace by copying the current holder */
if ( ! mst->prvtptr )
{
mst->prvtptr = msr_duplicate (msr, 0);
if ( ! mst->prvtptr )
{
fprintf (stderr, "[%s] Error duplicate MSRecord for template\n", sacfile);
return -1;
}
/* Add blockettes 1000 & 1001 to template */
memset (&Blkt1000, 0, sizeof(struct blkt_1000_s));
msr_addblockette ((MSRecord *) mst->prvtptr, (char *) &Blkt1000,
sizeof(struct blkt_1001_s), 1000, 0);
memset (&Blkt1001, 0, sizeof(struct blkt_1001_s));
msr_addblockette ((MSRecord *) mst->prvtptr, (char *) &Blkt1001,
sizeof(struct blkt_1001_s), 1001, 0);
/* Add blockette 100 to template if requested */
if ( srateblkt )
{
memset (&Blkt100, 0, sizeof(struct blkt_100_s));
Blkt100.samprate = (float) msr->samprate;
msr_addblockette ((MSRecord *) mst->prvtptr, (char *) &Blkt100,
sizeof(struct blkt_100_s), 100, 0);
}
}
packtraces (1);
packedtraces += mstg->numtraces;
/* Write metadata to file if requested */
if ( mfp )
{
if ( verbose )
fprintf (stderr, "[%s] Writing metadata to %s\n", sacfile, metafile);
if ( writemetadata (&sh, msr->network, msr->station, msr->location, msr->channel,
msr->starttime, expmeta) )
{
fprintf (stderr, "Error writing metadata to file '%s'\n", metafile);
return -1;
}
}
/* Cleanup */
fclose (ifp);
if ( ofp && ! outputfile )
{
fclose (ofp);
ofp = 0;
}
if ( fdata )
free (fdata);
if ( idata )
free (idata);
msr->datasamples = 0;
if ( msr )
msr_free (&msr);
return 0;
} /* End of sac2group() */
/***************************************************************************
* parsesac:
*
* Parse a SAC file, autodetecting format dialect (ALPHA,
* binary, big or little endian). Results will be placed in the
* supplied SAC header struct and data (float sample array in host
* byte order). The data array will be allocated by this routine and
* must be free'd by the caller. The data array will contain the
* number of samples indicated in the SAC header (sh->npts).
*
* The format argument is interpreted as:
* 0 : Unknown, detection needed
* 1 : ALPHA
* 2 : Binary, byte order detection needed
* 3 : Binary, little endian
* 4 : Binary, big endian
*
* Returns number of data samples in file or -1 on failure.
***************************************************************************/
static int
parsesac (FILE *ifp, struct SACHeader *sh, float **data, int format,
int verbose, char *sacfile)
{
char fourc[4];
int swapflag = 0;
int rv;
/* Argument sanity */
if ( ! ifp || ! sh || ! data )
return -1;
/* Read the first 4 characters */
if ( fread (&fourc, 4, 1, ifp) < 1 )
return -1;
/* Determine if the file is ALPHA or binary SAC,
* if the first 4 characters are spaces assume ALPHA SAC */
if ( format == 0 )
{
if ( fourc[0] == ' ' && fourc[1] == ' ' && fourc[2] == ' ' && fourc[3] == ' ' )
format = 1;
else
format = 2; /* Byte order detection will occur below */
}
/* Rewind the file position pointer to the beginning */
rewind (ifp);
/* Read the header */
if ( format == 1 ) /* Process SAC ALPHA header */
{
if ( (rv = readalphaheader (ifp, sh)) )
{
fprintf (stderr, "[%s] Error parsing SAC ALPHA header at line %d\n",
sacfile, rv);
return -1;
}
}
else if ( format >= 2 && format <= 4 ) /* Process SAC binary header */
{
if ( readbinaryheader (ifp, sh, &format, &swapflag, verbose, sacfile) )
{
fprintf (stderr, "[%s] Error parsing SAC header\n", sacfile);
return -1;
}
}
else
{
fprintf (stderr, "[%s] Unrecognized format value: %d\n", sacfile, format);
return -1;
}
/* Fix up underspecified year values by adding 1900 */
if ( sh->nzyear >= 0 && sh->nzyear <= 200 )
{
if ( verbose )
fprintf (stderr, "[%s] Adding 1900 to underspecified year value (%d)\n", sacfile, sh->nzyear);
sh->nzyear += 1900;
}
/* Sanity check the start time */
if ( sh->nzyear < 1900 || sh->nzyear >3000 ||
sh->nzjday < 1 || sh->nzjday > 366 ||
sh->nzhour < 0 || sh->nzhour > 23 ||
sh->nzmin < 0 || sh->nzmin > 59 ||
sh->nzsec < 0 || sh->nzsec > 60 ||
sh->nzmsec < 0 || sh->nzmsec > 999999 )
{
fprintf (stderr, "[%s] Unrecognized format (not SAC?)\n", sacfile);
return -1;
}
if ( verbose )
{
if ( format == 1 )
fprintf (stderr, "[%s] Reading SAC ALPHA format\n", sacfile);
if ( format == 3 )
fprintf (stderr, "[%s] Reading SAC binary format (little-endian)\n", sacfile);
if ( format == 4 )
fprintf (stderr, "[%s] Reading SAC binary format (big-endian)\n", sacfile);
}
if ( verbose > 2 )
fprintf (stderr, "[%s] SAC header version number: %d\n", sacfile, sh->nvhdr);
if ( sh->nvhdr != 6 )
fprintf (stderr, "[%s] WARNING SAC header version (%d) not expected value of 6\n",
sacfile, sh->nvhdr);
if ( sh->npts <= 0 )
{
fprintf (stderr, "[%s] No data, number of samples: %d\n", sacfile, sh->npts);
return -1;
}
if ( sh->iftype != ITIME )
{
fprintf (stderr, "[%s] Data is not time series (IFTYPE=%d), cannot convert other types\n",
sacfile, sh->iftype);
return -1;
}
if ( ! sh->leven )
{
fprintf (stderr, "[%s] Data is not evenly spaced (LEVEN not true), cannot convert\n", sacfile);
return -1;
}
/* Allocate space for data samples */
*data = (float *) malloc (sizeof(float) * sh->npts);
memset (*data, 0, (sizeof(float) * sh->npts));
/* Read the data samples */
if ( format == 1 ) /* Process SAC ALPHA data */
{
if ( (rv = readalphadata (ifp, *data, sh->npts)) )
{
fprintf (stderr, "[%s] Error parsing SAC ALPHA data at line %d\n",
sacfile, rv);
return -1;
}
}
else if ( format >= 2 && format <= 4 ) /* Process SAC binary data */
{
if ( readbinarydata (ifp, *data, sh->npts, swapflag, verbose, sacfile) )
{
fprintf (stderr, "[%s] Error reading SAC data samples\n", sacfile);
return -1;
}
}
else
{
fprintf (stderr, "[%s] Unrecognized format value: %d\n", sacfile, format);
return -1;
}
return sh->npts;
} /* End of parsesac() */
/***************************************************************************
* readbinaryheader:
*
* Read a binary header from a file and parse into a SAC header
* struct. Also determines byte order and sets the swap flag unless
* already dictated by the format.
*
* Returns 0 on sucess or -1 on failure.
***************************************************************************/
static int
readbinaryheader (FILE *ifp, struct SACHeader *sh, int *format,
int *swapflag, int verbose, char *sacfile)
{
int bigendianhost;
int32_t hdrver;
/* Read the binary header into memory */
if ( fread (sh, sizeof(struct SACHeader), 1, ifp) != 1 )
{
fprintf (stderr, "[%s] Could not read SAC header from file\n", sacfile);
if ( ferror (ifp) )
fprintf (stderr, "[%s] Error reading from file\n", sacfile);
return -1;
}
/* Determine if host is big-endian */
bigendianhost = ms_bigendianhost();
*swapflag = 0;
/* Test byte order using the header version if unknown */
/* Also set the swapflag appropriately */
if ( *format == 2 )
{
memcpy (&hdrver, &sh->nvhdr, 4);
if ( hdrver < 1 || hdrver > 10 )
{
ms_gswap4 (&hdrver);
if ( hdrver < 1 || hdrver > 10 )
{
fprintf (stderr, "[%s] Cannot determine byte order (not SAC?)\n", sacfile);
return -1;
}
*format = ( bigendianhost ) ? 3 : 4;
*swapflag = 1;
}
else
{
*format = ( bigendianhost ) ? 4 : 3;
}
}
else if ( *format == 3 && bigendianhost ) *swapflag = 1;
else if ( *format == 4 && ! bigendianhost ) *swapflag = 1;
if ( verbose > 1 )
{
if ( *swapflag )
fprintf (stderr, "[%s] Byte swapping required\n", sacfile);
else
fprintf (stderr, "[%s] Byte swapping NOT required\n", sacfile);
}
/* Byte swap all values in header */
if ( *swapflag )
swapsacheader (sh);
return 0;
} /* End of readbinaryheader() */
/***************************************************************************
* readbinarydata:
*
* Read binary data from a file and add to an array, the array
* must already be allocated with datacnt floats.
*
* Returns 0 on sucess or -1 on failure.
***************************************************************************/
static int
readbinarydata (FILE *ifp, float *data, int datacnt, int swapflag,
int verbose, char *sacfile)
{
int samplesread = 0;
int dataidx;
/* Read in data samples */
if ( (samplesread = fread (data, sizeof(float), datacnt, ifp)) != datacnt )
{
fprintf (stderr, "[%s] Only read %d of %d expected data samples\n",
sacfile, samplesread, datacnt);
return -1;
}
/* Swap data samples */
if ( swapflag )
{
for ( dataidx = 0; dataidx < datacnt; dataidx++ )
{
ms_gswap4 (data + dataidx);
}
}
return 0;
} /* End of readbinarydata() */
/***************************************************************************
* readalphaheader:
*
* Read a alphanumeric header from a file and parse into a SAC header
* struct.
*
* Returns 0 on sucess or a positive number indicating line number of
* parsing failure.
***************************************************************************/
static int
readalphaheader (FILE *ifp, struct SACHeader *sh)
{
char line[1025];
int linecnt = 1; /* The header starts at line 1 */
int lineidx;
int count;
int hvidx = 0;
char *cp;
if ( ! ifp || ! sh )
return -1;
/* The first 14 lines x 5 values are floats */
for (lineidx=0; lineidx < 14; lineidx++)
{
if ( ! fgets(line, sizeof(line), ifp) )
return linecnt;
count = sscanf (line, " %f %f %f %f %f ", (float *) sh + hvidx,
(float *) sh + hvidx + 1, (float *) sh + hvidx + 2,
(float *) sh + hvidx + 3, (float *) sh + hvidx + 4);
if ( count != 5 )
return linecnt;
hvidx += 5;
linecnt++;
}
/* The next 8 lines x 5 values are integers */
for (lineidx=0; lineidx < 8; lineidx++)
{
if ( ! fgets(line, sizeof(line), ifp) )
return linecnt;
count = sscanf (line, " %d %d %d %d %d ", (int32_t *) sh + hvidx,
(int32_t *) sh + hvidx + 1, (int32_t *) sh + hvidx + 2,
(int32_t *) sh + hvidx + 3, (int32_t *) sh + hvidx + 4);
if ( count != 5 )
return linecnt;
hvidx += 5;
linecnt++;
}
/* Set pointer to start of string variables */
cp = (char *) sh + (hvidx * 4);
/* The next 8 lines each contain 24 bytes of string data */
for (lineidx=0; lineidx < 8; lineidx++)
{
memset (line, 0, sizeof(line));
if ( ! fgets(line, sizeof(line), ifp) )
return linecnt;
memcpy (cp, line, 24);
cp += 24;
linecnt++;
}
/* Make sure each of the 23 string variables are left justified */
cp = (char *) sh + (hvidx * 4);
for (count=0; count < 24; count++)
{
int ridx, widx, width;
char *fcp;
/* Each string variable is 8 characters with one exception */
if ( count != 1 )
{
width = 8;
}
else
{
width = 16;
count++;
}
/* Pointer to field */
fcp = cp + (count * 8);
/* Find first character that is not a space */
ridx = 0;
while ( *(fcp + ridx) == ' ' )
ridx++;
/* Remove any leading spaces */
if ( ridx > 0 )
{
for (widx=0; widx < width; widx++, ridx++)
{
if ( ridx < width )
*(fcp + widx) = *(fcp + ridx);
else
*(fcp + widx) = ' ';
}
}
}
return 0;
} /* End of readalphaheader() */
/***************************************************************************
* readalphadata:
*
* Read a alphanumeric data from a file and add to an array, the array
* must already be allocated with datacnt floats.
*
* Returns 0 on sucess or a positive number indicating line number of
* parsing failure.
***************************************************************************/
static int
readalphadata (FILE *ifp, float *data, int datacnt)
{
char line[1025];
int linecnt = 31; /* Data samples start on line 31 */
int samplesread = 0;
int count;
int dataidx = 0;
if ( ! ifp || ! data || ! datacnt)
return -1;
/* Each data line should contain 5 floats unless the last */
for (;;)
{
if ( ! fgets(line, sizeof(line), ifp) )
return linecnt;
count = sscanf (line, " %f %f %f %f %f ", (float *) data + dataidx,
(float *) data + dataidx + 1, (float *) data + dataidx + 2,
(float *) data + dataidx + 3, (float *) data + dataidx + 4);
samplesread += count;
if ( samplesread >= datacnt )
break;
else if ( count != 5 )
return linecnt;
dataidx += 5;
linecnt++;
}
return 0;
} /* End of readalphadata() */
/***************************************************************************
* swapsacheader:
*
* Byte swap all multi-byte quantities (floats and ints) in SAC header
* struct.
*
* Returns 0 on sucess and -1 on failure.
***************************************************************************/
static int
swapsacheader (struct SACHeader *sh)
{
int32_t *ip;
int idx;
if ( ! sh )
return -1;
for ( idx=0; idx < (NUMFLOATHDR + NUMINTHDR); idx++ )
{
ip = (int32_t *) sh + idx;
ms_gswap4 (ip);
}
return 0;
} /* End of swapsacheader() */
/***************************************************************************
* writemetadata:
*
* Write a single line of metadata into the metadata output file
* containing the following fields comma-separated in this order:
*
* Network (supplied, originally knetwk)
* Station (supplied, originally kstnm)
* Location (supplied, originally khole)
* Channel (supplied, originally kcmpnm)
* Latitude (stla)
* Longitude (stlo)
* Elevation (stel) [not currently used by SAC]
* Depth (stdp) [not currently used by SAC]
* Component Azimuth (cmpaz), degrees clockwise from north
* Component Incident Angle (cmpinc), degrees from vertical
* Instrument Name (kinst)
* Scale Factor (scale)
* Scale Frequency, unknown, will be empty
* Scale Units, unknown, will be empty
* Sampling rate, in samples per second (1/delta)
* Start time, set to start time
* End time, set to end time
*
* If the expanded argument is true the following fields will also be
* included:
*
* Event Name (kevnm)
* User string 0 (kuser0)
* User string 1 (kuser1)
* User string 2 (kuser2)
*
* Returns 0 on sucess and -1 on failure.
***************************************************************************/
static int
writemetadata (struct SACHeader *sh, char *network, char *station,
char *location, char *channel, hptime_t starttime, int expanded)
{
static flag wroteheader = 0;
hptime_t endtime;
char string[50];
char *cp;
if ( ! sh || ! mfp )
return -1;
if ( ! wroteheader )
{
wroteheader = 1;
if ( ! fprintf (mfp, "#Net,Sta,Loc,Chan,Lat,Lon,Elev,Depth,Az,Inc,Inst,Scale,ScaleFreq,ScaleUnits,SampleRate,Start,End") )
{
fprintf (stderr, "Error writing to metadata output file\n");
return -1;
}
if ( expanded )
if ( ! fprintf (mfp, ",Event,String0,String1,String2") )
{
fprintf (stderr, "Error writing to metadata output file\n");
return -1;
}
fprintf (mfp, "\n");
}
/* LINE: Net,Sta,Loc,Chan,Lat,Lon,Elev,Dep,Az,Inc,Inst,Scale,ScaleFreq,ScaleUnits,SampleRate,Start,End[,Event,String0,String1,String2] */
/* Write the source parameters */
if ( ! fprintf (mfp, "%s,%s,%s,%s,", network, station, location, channel) )
{
fprintf (stderr, "Error writing to metadata output file\n");
return -1;
}
/* Write lat, lon, elev, depth, azimuth, incident, instrument, scale */
if ( sh->stla != FUNDEF ) fprintf (mfp, "%.5f,", sh->stla);
else fprintf (mfp, ",");
if ( sh->stlo != FUNDEF ) fprintf (mfp, "%.5f,", sh->stlo);
else fprintf (mfp, ",");
if ( sh->stel != FUNDEF ) fprintf (mfp, "%g,", sh->stel);
else fprintf (mfp, ",");
if ( sh->stdp != FUNDEF ) fprintf (mfp, "%g,", sh->stdp);
else fprintf (mfp, ",");
if ( sh->cmpaz != FUNDEF ) fprintf (mfp, "%g,", sh->cmpaz);
else fprintf (mfp, ",");
if ( sh->cmpinc != FUNDEF ) fprintf (mfp, "%g,", sh->cmpinc);
else fprintf (mfp, ",");
if ( strncmp (SUNDEF, sh->kinst, 6) )
{
memcpy (string, sh->kinst, 8); string[8] = '\0';
for (cp = &string[7]; cp >= string && *cp == ' '; cp-- ) { *cp = '\0'; }
fprintf (mfp, "%s,", string);
}
else fprintf (mfp, ",");
if ( sh->scale != FUNDEF ) fprintf (mfp, "%.g,", sh->scale);
else fprintf (mfp, ",");
/* Add blanks for ScaleFreq and ScaleUnits which we do not know */
fprintf (mfp, ",,");
/* Add sampling rate */
if ( sh->delta != FUNDEF ) fprintf (mfp, "%g,", 1.0/sh->delta);
else fprintf (mfp, ",");
/* Add start time */
ms_hptime2isotimestr (starttime, string, 0);
fprintf (mfp, "%s,", string);
/* Calculate and add end time */
endtime = starttime + (((sh->npts - 1) * sh->delta) * HPTMODULUS);
ms_hptime2isotimestr (endtime, string, 0);
fprintf (mfp, "%s", string);
/* Add expanded set of fields if requested */
if ( expanded )
{
fprintf (mfp, ",");
if ( strncmp (SUNDEF, sh->kevnm, 6) )
{
memcpy (string, sh->kevnm, 16); string[16] = '\0';
for (cp = &string[15]; cp >= string && *cp == ' '; cp-- ) { *cp = '\0'; }
fprintf (mfp, "%s,", string);
}
else fprintf (mfp, ",");
if ( strncmp (SUNDEF, sh->kuser0, 6) )
{
memcpy (string, sh->kuser0, 8); string[8] = '\0';
for (cp = &string[7]; cp >= string && *cp == ' '; cp-- ) { *cp = '\0'; }
fprintf (mfp, "%s,", string);
}
else fprintf (mfp, ",");
if ( strncmp (SUNDEF, sh->kuser1, 6) )
{
memcpy (string, sh->kuser1, 8); string[8] = '\0';
for (cp = &string[7]; cp >= string && *cp == ' '; cp-- ) { *cp = '\0'; }
fprintf (mfp, "%s,", string);
}
else fprintf (mfp, ",");
if ( strncmp (SUNDEF, sh->kuser2, 6) )
{
memcpy (string, sh->kuser2, 8); string[8] = '\0';
for (cp = &string[7]; cp >= string && *cp == ' '; cp-- ) { *cp = '\0'; }
fprintf (mfp, "%s", string);
}
}
fprintf (mfp, "\n");
return 0;
} /* End of writemetadata() */
/***************************************************************************
* parameter_proc:
* Process the command line parameters.
*
* Returns 0 on success, and -1 on failure.
***************************************************************************/
static int
parameter_proc (int argcount, char **argvec)
{
int optind;
/* Process all command line arguments */
for (optind = 1; optind < argcount; optind++)
{
if (strcmp (argvec[optind], "-V") == 0)
{
fprintf (stderr, "%s version: %s\n", PACKAGE, VERSION);
exit (0);
}
else if (strcmp (argvec[optind], "-h") == 0)
{
usage();
exit (0);
}
else if (strncmp (argvec[optind], "-v", 2) == 0)
{
verbose += strspn (&argvec[optind][1], "v");
}
else if (strcmp (argvec[optind], "-S") == 0)
{
srateblkt = 1;
}
else if (strcmp (argvec[optind], "-n") == 0)
{
forcenet = getoptval(argcount, argvec, optind++);
}
else if (strcmp (argvec[optind], "-t") == 0)
{
forcesta = getoptval(argcount, argvec, optind++);
}
else if (strcmp (argvec[optind], "-l") == 0)
{
forceloc = getoptval(argcount, argvec, optind++);
}
else if (strcmp (argvec[optind], "-c") == 0)
{
forcechan = getoptval(argcount, argvec, optind++);
}
else if (strcmp (argvec[optind], "-r") == 0)
{
packreclen = strtoul (getoptval(argcount, argvec, optind++), NULL, 10);
}
else if (strcmp (argvec[optind], "-e") == 0)
{
encoding = strtoul (getoptval(argcount, argvec, optind++), NULL, 10);
}
else if (strcmp (argvec[optind], "-b") == 0)
{
byteorder = strtoul (getoptval(argcount, argvec, optind++), NULL, 10);
}
else if (strcmp (argvec[optind], "-o") == 0)
{
outputfile = getoptval(argcount, argvec, optind++);
}
else if (strcmp (argvec[optind], "-m") == 0)
{
metafile = getoptval(argcount, argvec, optind++);
}
else if (strcmp (argvec[optind], "-me") == 0)
{
expmeta = 1;
}
else if (strcmp (argvec[optind], "-s") == 0)
{
datascaling = strtoull (getoptval(argcount, argvec, optind++), NULL, 10);
}
else if (strcmp (argvec[optind], "-f") == 0)
{
sacformat = strtoul (getoptval(argcount, argvec, optind++), NULL, 10);
}
else if (strncmp (argvec[optind], "-", 1) == 0 &&
strlen (argvec[optind]) > 1 )
{
fprintf(stderr, "Unknown option: %s\n", argvec[optind]);
exit (1);
}
else
{
addnode (&filelist, NULL, argvec[optind]);
}
}
/* Make sure an input files were specified */
if ( filelist == 0 )
{
fprintf (stderr, "No input files were specified\n\n");
fprintf (stderr, "%s version %s\n\n", PACKAGE, VERSION);
fprintf (stderr, "Try %s -h for usage\n", PACKAGE);
exit (1);
}
/* Report the program version */
if ( verbose )
fprintf (stderr, "%s version: %s\n", PACKAGE, VERSION);
/* Check the input files for any list files, if any are found
* remove them from the list and add the contained list */
if ( filelist )
{
struct listnode *prevln, *ln;
char *lfname;
prevln = ln = filelist;
while ( ln != 0 )
{
lfname = ln->data;
if ( *lfname == '@' )
{
/* Remove this node from the list */
if ( ln == filelist )
filelist = ln->next;
else
prevln->next = ln->next;
/* Skip the '@' first character */
if ( *lfname == '@' )
lfname++;
/* Read list file */
readlistfile (lfname);
/* Free memory for this node */
if ( ln->key )
free (ln->key);
free (ln->data);
free (ln);
}
else
{
prevln = ln;
}
ln = ln->next;
}
}
return 0;
} /* End of parameter_proc() */
/***************************************************************************
* getoptval:
* Return the value to a command line option; checking that the value is
* itself not an option (starting with '-') and is not past the end of
* the argument list.
*
* argcount: total arguments in argvec
* argvec: argument list
* argopt: index of option to process, value is expected to be at argopt+1
*
* Returns value on success and exits with error message on failure
***************************************************************************/
static char *
getoptval (int argcount, char **argvec, int argopt)
{
if ( argvec == NULL || argvec[argopt] == NULL ) {
fprintf (stderr, "getoptval(): NULL option requested\n");
exit (1);
return 0;
}
/* Special case of '-o -' usage */
if ( (argopt+1) < argcount && strcmp (argvec[argopt], "-o") == 0 )
if ( strcmp (argvec[argopt+1], "-") == 0 )
return argvec[argopt+1];
if ( (argopt+1) < argcount && *argvec[argopt+1] != '-' )
return argvec[argopt+1];
fprintf (stderr, "Option %s requires a value\n", argvec[argopt]);
exit (1);
return 0;
} /* End of getoptval() */
/***************************************************************************
* readlistfile:
*
* Read a list of files from a file and add them to the filelist for
* input data. The filename is expected to be the last
* space-separated field on the line.
*
* Returns the number of file names parsed from the list or -1 on error.
***************************************************************************/
static int
readlistfile (char *listfile)
{
FILE *fp;
char line[1024];
char *ptr;
int filecnt = 0;
char filename[1024];
char *lastfield = 0;
int fields = 0;
int wspace;
/* Open the list file */
if ( (fp = fopen (listfile, "rb")) == NULL )
{
if (errno == ENOENT)
{
fprintf (stderr, "Could not find list file %s\n", listfile);
return -1;
}
else
{
fprintf (stderr, "Error opening list file %s: %s\n",
listfile, strerror (errno));
return -1;
}
}
if ( verbose )
fprintf (stderr, "Reading list of input files from %s\n", listfile);
while ( (fgets (line, sizeof(line), fp)) != NULL)
{
/* Truncate line at first \r or \n, count space-separated fields
* and track last field */
fields = 0;
wspace = 0;
ptr = line;
while ( *ptr )
{
if ( *ptr == '\r' || *ptr == '\n' || *ptr == '\0' )
{
*ptr = '\0';
break;
}
else if ( *ptr != ' ' )
{
if ( wspace || ptr == line )
{
fields++; lastfield = ptr;
}
wspace = 0;
}
else
{
wspace = 1;
}
ptr++;
}
/* Skip empty lines */
if ( ! lastfield )
continue;
if ( fields >= 1 && fields <= 3 )
{
fields = sscanf (lastfield, "%s", filename);
if ( fields != 1 )
{
fprintf (stderr, "Error parsing file name from: %s\n", line);
continue;
}
if ( verbose > 1 )
fprintf (stderr, "Adding '%s' to input file list\n", filename);
addnode (&filelist, NULL, filename);
filecnt++;
continue;
}
}
fclose (fp);
return filecnt;
} /* End readlistfile() */
/***************************************************************************
* addnode:
*
* Add node to the specified list.
***************************************************************************/
static void
addnode (struct listnode **listroot, char *key, char *data)
{
struct listnode *lastlp, *newlp;
if ( data == NULL )
{
fprintf (stderr, "addnode(): No file name specified\n");
return;
}
lastlp = *listroot;
while ( lastlp != 0 )
{
if ( lastlp->next == 0 )
break;
lastlp = lastlp->next;
}
newlp = (struct listnode *) malloc (sizeof (struct listnode));
memset (newlp, 0, sizeof (struct listnode));
if ( key ) newlp->key = strdup(key);
else newlp->key = key;
if ( data) newlp->data = strdup(data);
else newlp->data = data;
newlp->next = 0;
if ( lastlp == 0 )
*listroot = newlp;
else
lastlp->next = newlp;
} /* End of addnode() */
/***************************************************************************
* record_handler:
* Saves passed records to the output file.
***************************************************************************/
static void
record_handler (char *record, int reclen, void *handlerdata)
{
if ( fwrite(record, reclen, 1, ofp) != 1 )
{
fprintf (stderr, "Error writing to output file\n");
}
} /* End of record_handler() */
/***************************************************************************
* usage:
* Print the usage message and exit.
***************************************************************************/
static void
usage (void)
{
fprintf (stderr, "%s version: %s\n\n", PACKAGE, VERSION);
fprintf (stderr, "Convert SAC waveform data to Mini-SEED.\n\n");
fprintf (stderr, "Usage: %s [options] file1 [file2 file3 ...]\n\n", PACKAGE);
fprintf (stderr,
" ## Options ##\n"
" -V Report program version\n"
" -h Show this usage message\n"
" -v Be more verbose, multiple flags can be used\n"
" -S Include SEED blockette 100 for very irrational sample rates\n"
" -n netcode Specify the SEED network code, default is KNETWK header value\n"
" -t stacode Specify the SEED station code, default is KSTNM header value\n"
" -l locid Specify the SEED location ID, default is KHOLE header value\n"
" -c chancodes Specify the SEED channel codes, default is KCMPNM header value\n"
" -r bytes Specify record length in bytes for packing, default: 4096\n"
" -e encoding Specify SEED encoding format for packing, default: 11 (Steim2)\n"
" -b byteorder Specify byte order for packing, MSBF: 1 (default), LSBF: 0\n"
" -o outfile Specify the output file, default is .mseed\n"
" -m metafile Specify the metadata output file\n"
" -me Write additional fields into the metadata output\n"
" -s factor Specify scaling factor for sample values, default is autoscale\n"
" -f format Specify input SAC file format (default is autodetect):\n"
" 0=autodetect, 1=alpha, 2=binary (detect byte order),\n"
" 3=binary (little-endian), 4=binary (big-endian)\n"
"\n"
" file(s) File(s) of SAC input data\n"
" If a file is prefixed with an '@' it is assumed to contain\n"
" a list of data files to be read\n"
"\n"
"Supported Mini-SEED encoding formats:\n"
" 3 : 32-bit integers, scaled\n"
" 4 : 32-bit floats (C float)\n"
" 10 : Steim 1 compression of scaled 32-bit integers\n"
" 11 : Steim 2 compression of scaled 32-bit integers\n"
"\n"
"For any of the non-floating point encoding formats the data samples\n"
"will be scaled either by the specified scaling factor or autoscaling\n"
"where the magnitude of the maximum sample will be 6 digits.\n"
"\n");
} /* End of usage() */
sac2mseed-1.12+ds1/src/sacformat.h 0000664 0000000 0000000 00000036512 13225712270 0016715 0 ustar 00root root 0000000 0000000 #ifndef SAC_FORMAT_H
#define SAC_FORMAT_H
/* Version: 2006.137 */
#ifdef __cplusplus
extern "C" {
#endif
#define REGCONV 100
#define SACHEADERLEN 632 /* SAC header length in bytes (only version 6?) */
#define NUMFLOATHDR 70 /* Number of float header variables, 4 bytes each */
#define NUMINTHDR 40 /* Number of integer header variables, 4 bytes each */
#define NUMSTRHDR 23 /* Number of string header variables, 22x8 bytes + 1x16 bytes */
/* Undefined values for float, integer and string header variables */
#define FUNDEF -12345.0
#define IUNDEF -12345
#define SUNDEF "-12345 "
/* SAC header structure as it exists in binary SAC files */
struct SACHeader
{
float delta; /* RF time increment, sec */
float depmin; /* minimum amplitude */
float depmax; /* maximum amplitude */
float scale; /* amplitude scale factor */
float odelta; /* observed time inc */
float b; /* RD initial value, time */
float e; /* RD final value, time */
float o; /* event start, sec < nz. */
float a; /* 1st arrival time */
float fmt; /* internal use */
float t0; /* user-defined time pick */
float t1; /* user-defined time pick */
float t2; /* user-defined time pick */
float t3; /* user-defined time pick */
float t4; /* user-defined time pick */
float t5; /* user-defined time pick */
float t6; /* user-defined time pick */
float t7; /* user-defined time pick */
float t8; /* user-defined time pick */
float t9; /* user-defined time pick */
float f; /* event end, sec > nz */
float resp0; /* instrument respnse parm*/
float resp1; /* instrument respnse parm*/
float resp2; /* instrument respnse parm*/
float resp3; /* instrument respnse parm*/
float resp4; /* instrument respnse parm*/
float resp5; /* instrument respnse parm*/
float resp6; /* instrument respnse parm*/
float resp7; /* instrument respnse parm*/
float resp8; /* instrument respnse parm*/
float resp9; /* instrument respnse parm*/
float stla; /* T station latititude */
float stlo; /* T station longitude */
float stel; /* T station elevation, m */
float stdp; /* T station depth, m */
float evla; /* event latitude */
float evlo; /* event longitude */
float evel; /* event elevation */
float evdp; /* event depth */
float mag; /* reserved for future use*/
float user0; /* available to user */
float user1; /* available to user */
float user2; /* available to user */
float user3; /* available to user */
float user4; /* available to user */
float user5; /* available to user */
float user6; /* available to user */
float user7; /* available to user */
float user8; /* available to user */
float user9; /* available to user */
float dist; /* stn-event distance, km */
float az; /* event-stn azimuth */
float baz; /* stn-event azimuth */
float gcarc; /* stn-event dist, degrees*/
float sb; /* internal use */
float sdelta; /* internal use */
float depmen; /* mean value, amplitude */
float cmpaz; /* T component azimuth */
float cmpinc; /* T component inclination */
float xminimum; /* reserved for future use*/
float xmaximum; /* reserved for future use*/
float yminimum; /* reserved for future use*/
float ymaximum; /* reserved for future use*/
float unused6; /* reserved for future use*/
float unused7; /* reserved for future use*/
float unused8; /* reserved for future use*/
float unused9; /* reserved for future use*/
float unused10; /* reserved for future use*/
float unused11; /* reserved for future use*/
float unused12; /* reserved for future use*/
int32_t nzyear; /* F zero time of file, yr */
int32_t nzjday; /* F zero time of file, day */
int32_t nzhour; /* F zero time of file, hr */
int32_t nzmin; /* F zero time of file, min */
int32_t nzsec; /* F zero time of file, sec */
int32_t nzmsec; /* F zero time of file, millisec*/
int32_t nvhdr; /* internal use (version) */
int32_t norid; /* origin ID */
int32_t nevid; /* event ID */
int32_t npts; /* RF number of samples */
int32_t nsnpts; /* internal use */
int32_t nwfid; /* waveform ID */
int32_t nxsize; /* reserved for future use*/
int32_t nysize; /* reserved for future use*/
int32_t unused15; /* reserved for future use*/
int32_t iftype; /* RA type of file */
int32_t idep; /* type of amplitude */
int32_t iztype; /* zero time equivalence */
int32_t unused16; /* reserved for future use*/
int32_t iinst; /* recording instrument */
int32_t istreg; /* stn geographic region */
int32_t ievreg; /* event geographic region*/
int32_t ievtyp; /* event type */
int32_t iqual; /* quality of data */
int32_t isynth; /* synthetic data flag */
int32_t imagtyp; /* reserved for future use*/
int32_t imagsrc; /* reserved for future use*/
int32_t unused19; /* reserved for future use*/
int32_t unused20; /* reserved for future use*/
int32_t unused21; /* reserved for future use*/
int32_t unused22; /* reserved for future use*/
int32_t unused23; /* reserved for future use*/
int32_t unused24; /* reserved for future use*/
int32_t unused25; /* reserved for future use*/
int32_t unused26; /* reserved for future use*/
int32_t leven; /* RA data-evenly-spaced flag*/
int32_t lpspol; /* station polarity flag */
int32_t lovrok; /* overwrite permission */
int32_t lcalda; /* calc distance, azimuth */
int32_t unused27; /* reserved for future use*/
char kstnm[8]; /* F station name */
char kevnm[16]; /* event name */
char khole[8]; /* man-made event name */
char ko[8]; /* event origin time id */
char ka[8]; /* 1st arrival time ident */
char kt0[8]; /* time pick 0 ident */
char kt1[8]; /* time pick 1 ident */
char kt2[8]; /* time pick 2 ident */
char kt3[8]; /* time pick 3 ident */
char kt4[8]; /* time pick 4 ident */
char kt5[8]; /* time pick 5 ident */
char kt6[8]; /* time pick 6 ident */
char kt7[8]; /* time pick 7 ident */
char kt8[8]; /* time pick 8 ident */
char kt9[8]; /* time pick 9 ident */
char kf[8]; /* end of event ident */
char kuser0[8]; /* available to user */
char kuser1[8]; /* available to user */
char kuser2[8]; /* available to user */
char kcmpnm[8]; /* F component name */
char knetwk[8]; /* network name */
char kdatrd[8]; /* date data read */
char kinst[8]; /* instrument name */
};
/* A SAC header null value initializer
* Usage: struct SACHeader sh = NullSACHeader; */
#define NullSACHeader { \
-12345.0, -12345.0, -12345.0, -12345.0, -12345.0, \
-12345.0, -12345.0, -12345.0, -12345.0, -12345.0, \
-12345.0, -12345.0, -12345.0, -12345.0, -12345.0, \
-12345.0, -12345.0, -12345.0, -12345.0, -12345.0, \
-12345.0, -12345.0, -12345.0, -12345.0, -12345.0, \
-12345.0, -12345.0, -12345.0, -12345.0, -12345.0, \
-12345.0, -12345.0, -12345.0, -12345.0, -12345.0, \
-12345.0, -12345.0, -12345.0, -12345.0, -12345.0, \
-12345.0, -12345.0, -12345.0, -12345.0, -12345.0, \
-12345.0, -12345.0, -12345.0, -12345.0, -12345.0, \
-12345.0, -12345.0, -12345.0, -12345.0, -12345.0, \
-12345.0, -12345.0, -12345.0, -12345.0, -12345.0, \
-12345.0, -12345.0, -12345.0, -12345.0, -12345.0, \
-12345.0, -12345.0, -12345.0, -12345.0, -12345.0, \
-12345, -12345, -12345, -12345, -12345, \
-12345, -12345, -12345, -12345, -12345, \
-12345, -12345, -12345, -12345, -12345, \
-12345, -12345, -12345, -12345, -12345, \
-12345, -12345, -12345, -12345, -12345, \
-12345, -12345, -12345, -12345, -12345, \
-12345, -12345, -12345, -12345, -12345, \
-12345, -12345, -12345, -12345, -12345, \
{ '-','1','2','3','4','5',' ',' ' }, \
{ '-','1','2','3','4','5',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ' }, \
{ '-','1','2','3','4','5',' ',' ' },{ '-','1','2','3','4','5',' ',' ' }, \
{ '-','1','2','3','4','5',' ',' ' },{ '-','1','2','3','4','5',' ',' ' }, \
{ '-','1','2','3','4','5',' ',' ' },{ '-','1','2','3','4','5',' ',' ' }, \
{ '-','1','2','3','4','5',' ',' ' },{ '-','1','2','3','4','5',' ',' ' }, \
{ '-','1','2','3','4','5',' ',' ' },{ '-','1','2','3','4','5',' ',' ' }, \
{ '-','1','2','3','4','5',' ',' ' },{ '-','1','2','3','4','5',' ',' ' }, \
{ '-','1','2','3','4','5',' ',' ' },{ '-','1','2','3','4','5',' ',' ' }, \
{ '-','1','2','3','4','5',' ',' ' },{ '-','1','2','3','4','5',' ',' ' }, \
{ '-','1','2','3','4','5',' ',' ' },{ '-','1','2','3','4','5',' ',' ' }, \
{ '-','1','2','3','4','5',' ',' ' },{ '-','1','2','3','4','5',' ',' ' }, \
{ '-','1','2','3','4','5',' ',' ' } \
};
/* definitions of constants for SAC enumerated data values */
#define IREAL 0 /* undocumented */
#define ITIME 1 /* file: time series data */
#define IRLIM 2 /* file: real&imag spectrum */
#define IAMPH 3 /* file: ampl&phas spectrum */
#define IXY 4 /* file: gen'l x vs y data */
#define IUNKN 5 /* x data: unknown type */
/* zero time: unknown */
/* event type: unknown */
#define IDISP 6 /* x data: displacement (nm) */
#define IVEL 7 /* x data: velocity (nm/sec) */
#define IACC 8 /* x data: accel (cm/sec/sec)*/
#define IB 9 /* zero time: start of file */
#define IDAY 10 /* zero time: 0000 of GMT day*/
#define IO 11 /* zero time: event origin */
#define IA 12 /* zero time: 1st arrival */
#define IT0 13 /* zero time: user timepick 0*/
#define IT1 14 /* zero time: user timepick 1*/
#define IT2 15 /* zero time: user timepick 2*/
#define IT3 16 /* zero time: user timepick 3*/
#define IT4 17 /* zero time: user timepick 4*/
#define IT5 18 /* zero time: user timepick 5*/
#define IT6 19 /* zero time: user timepick 6*/
#define IT7 20 /* zero time: user timepick 7*/
#define IT8 21 /* zero time: user timepick 8*/
#define IT9 22 /* zero time: user timepick 9*/
#define IRADNV 23 /* undocumented */
#define ITANNV 24 /* undocumented */
#define IRADEV 25 /* undocumented */
#define ITANEV 26 /* undocumented */
#define INORTH 27 /* undocumented */
#define IEAST 28 /* undocumented */
#define IHORZA 29 /* undocumented */
#define IDOWN 30 /* undocumented */
#define IUP 31 /* undocumented */
#define ILLLBB 32 /* undocumented */
#define IWWSN1 33 /* undocumented */
#define IWWSN2 34 /* undocumented */
#define IHGLP 35 /* undocumented */
#define ISRO 36 /* undocumented */
/* Source types */
#define INUCL 37 /* event type: nuclear shot */
#define IPREN 38 /* event type: nuke pre-shot */
#define IPOSTN 39 /* event type: nuke post-shot*/
#define IQUAKE 40 /* event type: earthquake */
#define IPREQ 41 /* event type: foreshock */
#define IPOSTQ 42 /* event type: aftershock */
#define ICHEM 43 /* event type: chemical expl */
#define IOTHER 44 /* event type: other source */
#define IQB 72 /* Quarry Blast or mine expl. confirmed by quarry */
#define IQB1 73 /* Quarry or mine blast with designed shot information-ripple fired */
#define IQB2 74 /* Quarry or mine blast with observed shot information-ripple fired */
#define IQBX 75 /* Quarry or mine blast - single shot */
#define IQMT 76 /* Quarry or mining-induced events: tremors and rockbursts */
#define IEQ 77 /* Earthquake */
#define IEQ1 78 /* Earthquakes in a swarm or aftershock sequence */
#define IEQ2 79 /* Felt earthquake */
#define IME 80 /* Marine explosion */
#define IEX 81 /* Other explosion */
#define INU 82 /* Nuclear explosion */
#define INC 83 /* Nuclear cavity collapse */
#define IO_ 84 /* Other source of known origin */
#define IL 85 /* Local event of unknown origin */
#define IR 86 /* Regional event of unknown origin */
#define IT 87 /* Teleseismic event of unknown origin */
#define IU 88 /* Undetermined or conflicting information */
#define IEQ3 89 /* Damaging earthquake */
#define IEQ0 90 /* Probable earthquake */
#define IEX0 91 /* Probable explosion */
#define IQC 92 /* Mine collapse */
#define IQB0 93 /* Probable Mine Blast */
#define IGEY 94 /* Geyser */
#define ILIT 95 /* Light */
#define IMET 96 /* Meteoric Event */
#define IODOR 97 /* Odors */
#define IOS 103 /* Other source: Known origin*/
/* data quality: other problm*/
#define IGOOD 45 /* data quality: good */
#define IGLCH 46 /* data quality: has glitches*/
#define IDROP 47 /* data quality: has dropouts*/
#define ILOWSN 48 /* data quality: low s/n */
#define IRLDTA 49 /* data is real data */
#define IVOLTS 50 /* file: velocity (volts) */
/* Magnitude type and source */
#define IMB 52 /* Bodywave Magnitude */
#define IMS 53 /* Surface Magnitude */
#define IML 54 /* Local Magnitude */
#define IMW 55 /* Moment Magnitude */
#define IMD 56 /* Duration Magnitude */
#define IMX 57 /* User Defined Magnitude */
#define INEIC 58 /* INEIC */
#define IPDEQ 59 /* IPDE */
#define IPDEW 60 /* IPDE */
#define IPDE 61 /* IPDE */
#define IISC 62 /* IISC */
#define IREB 63 /* IREB */
#define IUSGS 64 /* IUSGS */
#define IBRK 65 /* IBRK */
#define ICALTECH 66 /* ICALTECH */
#define ILLNL 67 /* ILLNL */
#define IEVLOC 68 /* IEVLOC */
#define IJSOP 69 /* IJSOP */
#define IUSER 70 /* IUSER */
#define IUNKNOWN 71 /* IUNKNOWN */
#ifdef __cplusplus
}
#endif
#endif /* SAC_FORMAT_H */
sac2mseed-1.12+ds1/testdata/ 0000775 0000000 0000000 00000000000 13225712270 0015600 5 ustar 00root root 0000000 0000000 sac2mseed-1.12+ds1/testdata/TA.ELFS..LHZ.SAC 0000664 0000000 0000000 00000023770 13225712270 0017707 0 ustar 00root root 0000000 0000000 ? @ @MN @: E @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @$y"Bt D @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ { # E ` ELFS -12345 -12345 -12345 -12345 -12345 -12345 -12345 -12345 -12345 -12345 -12345 -12345 -12345 -12345 -12345 -12345 -12345 LHZ TA -12345 -12345 B 0 , P 4 B uC C B B B B A B B 8 B A C 2 B B 0 @ b GC GC A +C C h ]
C mC B B ^ lB C B B A $ A B xB 8 4 C ?C B 5C C A ? d A B ) 7 B C ` @ 2C ! XB C B f 0A B | $ C XB h %C @ @B tB A AC C + ` B B A