Commit bfd4fcdf authored by thomas.forbriger's avatar thomas.forbriger Committed by thomas.forbriger
Browse files

now tested for reading and writing

This is a legacy commit from before 2015-03-01.
It may be incomplete as well as inconsistent.
See COPYING.legacy and README.history for details.

SVN Path:
SVN Revision: 1433
SVN UUID:     67feda4a-a26e-11df-9d6e-31afc202ad0c
parent 9e4db0d8
# this is <Makefile>
# ----------------------------------------------------------------------------
# $Id: Makefile,v 1.5 2003-12-23 13:59:59 tforb Exp $
# $Id: Makefile,v 1.6 2003-12-30 17:18:52 tforb Exp $
# Copyright (c) 2003 by Thomas Forbriger (BFO Schiltach)
......@@ -23,7 +23,7 @@ CPPFLAGS=-I$(LOCINCLUDEDIR) $(FLAGS)
all: libsffxx.a doxybrief doxyfull
flist: Makefile $(wildcard *.h *.cc *.cfg)
flist: Makefile README $(wildcard *.h *.cc *.cfg)
echo $^ | tr ' ' '\n' | sort > $@
.PHONY: edit
......@@ -68,7 +68,7 @@ DOXYWWWPATH=$(HOME)/public_html/txt/cxx/libsffxx
doxyclean: ;/bin/rm -rfv doc docbrief
doc/%: doxyfull.cfg $(DOXYSRC)
mkdir -vp $(DOXYWWWPATH)
......@@ -94,7 +94,7 @@ doxyview: doxyfullview doxybrief
$(CXX) -o $@ $< -I$(LOCINCLUDEDIR) -lsffxx -ltime++ -lgsexx -laff \
tests: sfftest; $<; rm -fv $<
# ----- END OF Makefile -----
/*! \file README
* \brief documentation for libsffxx.a
* ----------------------------------------------------------------------------
* $Id: README,v 1.1 2003-12-30 17:18:52 tforb Exp $
* \author Thomas Forbriger
* \date 25/12/2003
* documentation for libsffxx.a
* Copyright (c) 2003 by Thomas Forbriger (BFO Schiltach)
* - 25/12/2003 V1.0 Thomas Forbriger
* ============================================================================
/*! \mainpage
The library is designed to provide a tool for SFF writing and reading.
It uses libgsexx for the GSE2 layer of SFF.
While libgsexx does not explicitely use an array class for time series,
libsffxx provides whole array reading and writing functionality.
For this purpose the library uses code from libaff.
A major advantage of the library is a feature of libgsexx.
With this code you can deal with time series of arbitrary length.
In particular no temporary character buffer for CM6 decoding and encoding is
needed (like it is in the Fortran library).
This means, on the other hand, that the library has no information to fill the
nchar field of the DAST line.
The library will write -1 to the nchar field of the DAST line.
You will need an updated version of libstuff or libsff (Fortran version) to
read data files written with libsffxx to Fortran code.
\section Contents
- \ref sec_main_dependencies
- \ref sec_main_structure
- \ref sec_main_structs
- \ref sec_main_header_classes
- \ref sec_main_waveform_io
- \ref sec_main_usage
- \ref sec_main_reading
- \ref sec_main_writing
\section sec_main_dependencies Dependencies
The library depends on other code libaries. They are:
\subsection libtime
You should link against libtime++.a
Date values in WID2 and SRCE structs are stored in libtime::TAbsoluteTime
The library uses the reading functions of libtime to decode time strings.
The header sffxx.h includes libtime++.h
\subsection libgsexx
You should link against libgsexx.a
The GSE2 layer of the library relies on the code provided by libgsexx.
The header sffxx.h includes gsexx.h
\subsection libaff
You should link against libaff.a
The reading and writing classes sff::InputWaveform and sff::OutputWaveform
as well as the sff::TraceHeader::scanseries member function deal with
series type containers as defined in libaff.
The header sffxx.h includes aff/series.h and aff/iterator.h
\subsection STL
The sff::FREE block struct uses an STL list of strings.
\section sec_main_structure Structure of the library modules
\subsection sec_main_structs SFF element structs
For each of the structural elements in an SFF file there exists a C++ struct
to represent the file contents:
- STAT line: sff::STAT
- FREE block: sff::FREE
- SRCE line: sff::SRCE
- DAST line: sff::DAST
- WID2 line: sff::WID2
- INFO line: sff::INFO
For each relevant entry in the data file there exists a corresponding member
data field in the struct.
The member data are read and modified by directly accessing the data fields.
However some member functions are provided together with the structs.
These member functions set default values (constructors), provide an ASCII
representation of the values as has to be written to the file (line and
write functions) and support reading from a file (read functions).
The WID2 struct will be mapped to a WID2 struct from the libgsexx upon
reading and writing.
There ist no struct for the DAT2 line and the CHK2 line, since both are
written and read through the DAT2read and DAT2write class from libgsexx.
\subsection sec_main_header_classes Header Classes
The classes sff::FileHeader and sff::TraceHeader glue SFF structs to a
Header data is filled either by creating a class object through one of the
constructors or through on of the file reading member functions.
The member data fields (SFF structs) are only provided for reading access.
The Header classes keep track on optional fields.
They are selected by using and appropriate constructor or by reading a file.
Member functions like sff::FileHeader::hasfree() tell you whether an
optional element is used and present.
While an sff::FileHeader can immediately be used for reading from and
writing to a data file, this is not the case for sff::TraceHeader.
The structure defintion for SFF files wants the optional trace header fields
to be placed after the the trace data (after the CHK2 line).
This means that the trace header must be written together with the data
through an sff::OutputWaveform object.
Use an sff::InputWaveform object for the same reason upon reading.
\subsection sec_main_waveform_io Waveform I/O tools
The class templates sff::OutputWaveform and sff::InputWaveform are used for
writing and reading SFF data together with the trace header.
They expect a time series to be contained in an aff::Series object and are
defined as class templates.
You may select your own numerical type for the aff::Series container.
Use sff::SkipWaveform to skip the trace data upon reading.
The sff::SkipWaveform will still provide the header information for the
skipped data block.
\subsection sec_main_exceptions Exceptions
The library uses the exception class GSE2::Terror from libgsexx to indicate
error conditions.
\section sec_main_usage HOWTO use the library
Examples for the usage of the library modules can be found in
\subsection sec_main_reading Reading from an SFF data file
First open a file input stream.
std::ifstream is("filename");
Then read the file header.
sff::FileHeader fileheader(is);
Optional blocks are read as well and you may check for them and access them
through the member functions of sff::FileHeader.
Next cycle through the traces as desired.
Each trace is read by creating an object of type sff::InputWaveform or
sff::InputWaveform<Tseries> inputwaveform(is);
sff::SkipWaveform<Tseries> skipwaveform(is);
Here Tseries should be an appropriate libaff container like
typedef aff::Series<double> Tseries;
You may read to existing objects as well:
is >> inputwaveform;
is >> skipwaveform;
After reading each trace, you may access the trace header object through
the sff::InputWaveform::header() or sff::SkipWaveform::header() member
functions and the time series through the sff::InputWaveform::series()
member function.
You should check for more traces through the sff::InputWaveform::last() and
sff::SkipWaveform::last() member functions.
The sff::InputWaveform::valid() and sff::SkipWaveform::valid() member
functions tell you whether the object already contains valid data, which is
not the case if the object was created through its default constructor and
has no data yet read into it.
\subsection sec_main_writing Writing to an SFF data file
For writing an SFF data file first open an output file stream.
std::ofstream os("filename");
Create the elements to combine to an sff::FileHeader object.
sff::SRCE srceline;;
srceline.type="Hammerblow";"2003/12/31 10:12:00");
sff::FREE filefree;
filefree.lines.push_back("Example for a free block.");
filefree.lines.puch_back("another line...");
The create an sff::FileHeader from them.
sff::FileHeader fileheader(srceline,filefree);
sff::FileHeader fileheader;
in the minimalistic case of no optional blocks.
The file header may be written directly through
os << fileheader;
No cycle through all traces.
First create all header elements needed for each trace.
sff::INFO infoline;
sff::FREE tracefree;
tracefree.lines.push_back("A FREE block for a waveform");
sff::WID2 wid2line;
The create an sff::TraceHeader from them.
bool last=true;
sff::TraceHeader traceheader(wid2line,infoline,tracefree,last);
sff::TraceHeader traceheader(wid2line);
in the minimalistic case of no optional blocks.
At this point you must specify whether this is the last trace in the file or
If you omit the last flag, it is implicitely set to false.
The trace header together with the time series is written to os through an
sff::OutputWaveform object, which may be instantiated just for this purpose:
os << sff::OutputWaveform<Tseries>(series, traceheader, sff::NM_maxdyn);
The sff::OutputWaveform object takes care about normalization of the time
series samples provided through the aff::Series object passed as argument
The way normalization is done, can be selected by passing on of the
sff::Enormmode flags to the constructor of sff::OutputWaveform.
/* ----- END OF README ----- */
......@@ -3,7 +3,7 @@
* ----------------------------------------------------------------------------
* $Id:,v 1.5 2003-12-23 15:47:22 tforb Exp $
* $Id:,v 1.6 2003-12-30 17:18:53 tforb Exp $
* \author Thomas Forbriger
* \date 21/12/2003
......@@ -19,7 +19,7 @@
"SFFTEST V1.0 test library modules"
"$Id:,v 1.5 2003-12-23 15:47:22 tforb Exp $"
"$Id:,v 1.6 2003-12-30 17:18:53 tforb Exp $"
#include <fstream>
#include <iostream>
......@@ -171,14 +171,14 @@ void test_waveform_normalizer()
void test_write_file()
char filename[]="junk.dat";
char filename[]="junk.sff";
cout << "test writing to file: " << filename << endl
<< "---------------------" << endl;
const int msamp=1000;
Tseries series(msamp);
for (int i=series.first(); i<=series.last(); i++)
{ series(i)=1.e6*sin(3.1415926*i*5/msamp); }
{ series(i)=10.*sin(3.1415926*i*5/msamp); }
sff::SRCE mysrce;;
......@@ -210,6 +210,106 @@ void test_write_file()
sff::TraceHeader hd(mywid2,myinfo,myfree,true);
os << sff::OutputWaveform<Tseries>(series, hd, sff::NM_ifneeded);
cout << "you will find the data in " << filename << endl;
void test_read_file()
char infile[]="junk.sff";
char outfile[]="junk2.sff";
cout << "test reading from file: " << infile << endl
<< "-----------------------" << endl;
char c;
std::cerr << "manipulate " << infile
<< " - if you like to..." << std::endl;
std::ifstream is(infile);
sff::FileHeader fhd(is);
std::ofstream os(outfile);
os << fhd;
if (fhd.hassrce())
cout << "SRCEtime: " << fhd.srce().date.timestring() << endl;
bool last=false;
while (!last)
sff::InputWaveform<Tseries> iwf;
try {
is >> iwf;
} catch (GSE2::Terror) {
cerr << "UUpppsss" << endl;
sff::WID2 wid2line(iwf.header().wid2());
cerr << wid2line.line();
Tseries series(iwf.series());
cerr << series.size() << " samples" << endl;
cerr << iwf.header().wid2().line() << endl;
os << sff::OutputWaveform<Tseries>(iwf.series(),
cout << "you will find the data in " << outfile << endl;
void test_skip_trace()
char outfile[]="junk3.sff";
cout << "test kipping every second trace" << endl
<< "-------------------------------" << endl;
cout << "enter name of input file: ";
std::string infile;
std::cin >> infile;
std::ifstream is(infile.c_str());
sff::FileHeader fhd(is);
std::ofstream os(outfile);
os << fhd;
if (fhd.hassrce())
cout << "SRCEtime: " << fhd.srce().date.timestring() << endl;
bool last=false;
while (!last)
sff::SkipWaveform swf(is);
cout << swf.header().wid2().line() << endl;
if (!last)
sff::InputWaveform<Tseries> iwf(is);
os << sff::OutputWaveform<Tseries>(iwf.series(),
Tseries dummy(5);
sff::TraceHeader ihd(swf.header());
sff::TraceHeader ohd(ihd.wid2(),true);
os << sff::OutputWaveform<Tseries>(dummy, ohd, sff::NM_one);
cout << "you will find the data in " << outfile << endl;
......@@ -220,6 +320,8 @@ int main(int iargc, char* argv[])
test_write_wrappers(); cout << endl;
test_waveform_normalizer(); cout << endl;
test_write_file(); cout << endl;
test_read_file(); cout << endl;
test_skip_trace(); cout << endl;
/* ----- END OF ----- */
......@@ -3,7 +3,7 @@
* ----------------------------------------------------------------------------
* $Id:,v 1.6 2003-12-23 16:37:29 tforb Exp $
* $Id:,v 1.7 2003-12-30 17:18:53 tforb Exp $
* \author Thomas Forbriger
* \date 21/12/2003
......@@ -19,7 +19,7 @@
"$Id:,v 1.6 2003-12-23 16:37:29 tforb Exp $"
"$Id:,v 1.7 2003-12-30 17:18:53 tforb Exp $"
#include <sffxx.h>
#include <gsexx.h>
......@@ -70,6 +70,13 @@ namespace sff {
// ----
/*! \struct STAT
* The STAT line is the first line in the file header.
* It contains the version of the library that wrote the file,
* a timestamp and flags indicating the presence of optional elements lika a
* FREE block or an SRCE line.
STAT::STAT(): hasfree(false), hassrce(false)
{ setstamp(libtime::now()); }
......@@ -171,17 +178,17 @@ namespace sff {
is >> datestring;
is >> timestring;
std::string fulldate("");
fulldate+=" ";
} // SRCE::read
......@@ -200,9 +207,10 @@ namespace sff {
if (this->hasfree) { code.append("F"); }
if (this->hasinfo) { code.append("I"); }
if (!this->last) { code.append("D"); }
// write -1 to nchar field in any case
sprintf(charline, "%-4s %10i %16.6E %-10s\n",
nchar, ampfac, code.c_str());
-1, ampfac, code.c_str());
std::string retval(charline);
} // DAST::line()
......@@ -241,15 +249,15 @@ namespace sff {
std::string lineID;
is >> lineID;
if (!helper::IDmatch<DAST>(lineID)) throw
GSE2::Terror("ERROR (DAST::read): missing DAST ID!");
if (!helper::IDmatch<FREE>(lineID)) throw
GSE2::Terror("ERROR (FREE::read): missing FREE ID!");
std::string theline("");
const int bufsize=81;
char inputline[bufsize];
while (!helper::IDmatch<DAST>(theline))
while (!helper::IDmatch<FREE>(theline))
......@@ -334,8 +342,8 @@ namespace sff {
std::string lineID;
is >> lineID;
if (!helper::IDmatch<DAST>(lineID)) throw
GSE2::Terror("ERROR (DAST::read): missing DAST ID!");
if (!helper::IDmatch<INFO>(lineID)) throw
GSE2::Terror("ERROR (INFO::read): missing INFO ID!");
char cschar;
is >> cschar;
......@@ -356,6 +364,13 @@ namespace sff {
if (Mstat.hassrce) { os << Msrce.line(); }
void FileHeader::read(std::istream& is)
if (Mstat.hasfree) {; }
if (Mstat.hassrce) {; }
// TraceHeader
// -----------
......@@ -372,6 +387,18 @@ namespace sff {
if (Mdast.hasinfo) { os << Minfo.line(); }
void TraceHeader::readheader(std::istream& is)
void TraceHeader::readtrailer(std::istream& is)
if (Mdast.hasfree) {; }
if (Mdast.hasinfo) {; }
// WaveformNormalizer
// ------------------
......@@ -410,6 +437,22 @@ namespace sff {
"library inconsistency!");
// SkipWaveform
// ------------
void SkipWaveform::read(std::istream& is)
int nsamples=Mheader.wid2().nsamples;
GSE2::waveform::TDAT2readCM6 freader(nsamples);
int idata;
for (int i=0; i<nsamples; i++)
{ idata=freader(is); }
} // SkipWaveform::read
} // namespace sff
/* ----- END OF ----- */
......@@ -3,7 +3,7 @@
* ----------------------------------------------------------------------------
* $Id: sffxx.h,v 1.6 2003-12-23 16:37:29 tforb Exp $
* $Id: sffxx.h,v 1.7 2003-12-30 17:18:53 tforb Exp $
* \author Thomas Forbriger
* \date 21/12/2003
......@@ -26,7 +26,7 @@
"TF_SFFXX_H V1.1 "
#define TF_SFFXX_H_CVSID \
"$Id: sffxx.h,v 1.6 2003-12-23 16:37:29 tforb Exp $"
"$Id: sffxx.h,v 1.7 2003-12-30 17:18:53 tforb Exp $"
......@@ -57,7 +57,7 @@ namespace sff {
enum Enormmode {
NM_one, //!< do not scale
NM_maxdyn, //!< scale for maximum dynamic range
NM_ifneeded //!< scale if dynamic range larger than limit
NM_ifneeded //!< scale if largest amplitude larger than limit
}; // enum Enormmode
......@@ -169,6 +169,11 @@ namespace sff {
FileHeader(std::istream& is) { read(is); }
void write(std::ostream&) const;