Waveform filter programs
deconv.cc
Go to the documentation of this file.
1 
36 #define DECONV_VERSION \
37  "DECONV V1.2 calculate response function by deconvolution"
38 
39 #include <string>
40 #include <iostream>
41 #include <fstream>
42 #include <sstream>
43 #include <tfxx/commandline.h>
44 #include <tfxx/xcmdline.h>
45 #include <tfxx/rangelist.h>
46 #include <tfxx/rangestring.h>
47 #include <tfxx/error.h>
48 #include <tsxx/tsxx.h>
49 #include <tsioxx/outputoperators.h>
50 #include <tsxx/tapers.h>
51 #include <tsxx/filter.h>
52 #include <tsioxx/inputoperators.h>
53 #include <fourier/fftwaff.h>
54 #include <datrwxx/readany.h>
55 #include <datrwxx/sff.h>
56 
57 using std::cout;
58 using std::cerr;
59 using std::endl;
60 
61 typedef aff::Series<double> Tseries;
62 typedef ts::sff::SFFTimeSeries<Tseries> Ttimeseries;
63 typedef fourier::fft::DRFFTWAFF Tfft;
64 
65 /*----------------------------------------------------------------------*/
66 
68 const char* const tracekey="t";
70 const char* keys[]={
72  tracekey,
73  0
74 }; // const char* keys[]
75 
76 /*----------------------------------------------------------------------*/
77 
78 // structure to hold values of command line options
79 struct Options {
82  std::string transformfile, inputformat;
83  bool demean, detrend;
85 }; // struct Options
86 
87 /*======================================================================*/
88 
89 int main(int iargc, char* argv[])
90 {
91 
92  // define usage information
93  char usage_text[]=
94  {
95  DECONV_VERSION "\n"
96  "usage: deconv [-v] [-o] [-damping=v] [-cosine=v]" "\n"
97  " [-transform=file] [-type f]" "\n"
98  " [-datetolerance f\n"
99  " [-demean[=n]] [-dtrend[=n]]" "\n"
100  " response input [t:l] [input [t:l]]" "\n"
101  " or: deconv --help|-h" "\n"
102  };
103 
104  // define full help text
105  char help_text[]=
106  {
107  "response impulse response of filter" "\n"
108  "input input time series" "\n"
109  " Several filenames together with trace selectors can be" "\n"
110  " used. The first to traces read will be used for the" "\n"
111  " analysis:" "\n"
112  " trace #1: input to filter" "\n"
113  " trace #2: output from filter" "\n"
114  "\n"
115  "-verbose be verbose" "\n"
116  "-overwrite overwrite existing output file" "\n"
117  "-damping v set damping to fraction v of average input energy" "\n"
118  "-cosine v use cosine taper, wherer 0<v<0.5 is the taper fraction" "\n"
119  "-datetolerance f\n"
120  " set a tolerance level f for comparison of date of\n"
121  " input time series; dates will be considered as different\n"
122  " if they differ by more than a fraction f of the average\n"
123  " sampling interval\n"
124  "-transform f write Fourier transform of impulse response to file \"f\"" "\n"
125  " 1st column: frequency\n"
126  " 2nd column: amplitude response\n"
127  " 3nd column: phase response\n"
128  " 4th column: amplitude response from Fourier amplitudes\n"
129  " without damping (ragularization)\n"
130  " 5th column: FFT of filter input signal\n"
131  " 6th column: FFT of filter output signal\n"
132  "-type f data input files have format \"f\"" "\n"
133  "-demean[=n] remove average (determined from n samples)" "\n"
134  "-detrend[=n] remove trend (determined from n samples)" "\n"
135  "\n"
136  "The program reads the input and output signals for a linear time" "\n"
137  "invariant filter from the SFF data files \"input\" and \"output\"." "\n"
138  "From these the impulse response function of the filter is" "\n"
139  "calulated by a stabilized deconvolution in the Fourier domain." "\n"
140  "The result will written to the SFF data file \"response\", where" "\n"
141  "the source time defines the origin." "\n"
142  "\n"
143  "The default taper is a cosine taper of 0.5 (i.e. Hanning)." "\n"
144  "Setting the cosine taper fraction to 0. will result in a" "\n"
145  "boxcar taper." "\n"
146  };
147 
148  // define commandline options
149  using namespace tfxx::cmdline;
150  static Declare options[]=
151  {
152  // 0: print help
153  {"help",arg_no,"-"},
154  // 1: verbose mode
155  {"verbose",arg_no,"-"},
156  // 2: overwrite mode
157  {"overwrite",arg_no,"-"},
158  // 3: remove average
159  {"demean",arg_opt,"0"},
160  // 4: cosine taper
161  {"cosine",arg_yes,"0.5"},
162  // 5: output file for transform
163  {"transform",arg_yes,"coeff.tab"},
164  // 6: damping
165  {"damping",arg_yes,"0.1"},
166  // 7: damping
167  {"type",arg_yes,"sff"},
168  // 8: remove trend
169  {"detrend",arg_opt,"0"},
170  // 9: set date and time tolerance when checking consistency
171  {"datetolerance",arg_yes,"0."},
172  {NULL}
173  };
174 
175  // no arguments? print usage...
176  if (iargc<2)
177  {
178  cerr << usage_text << endl;
179  exit(0);
180  }
181 
182  // collect options from commandline
183  Commandline cmdline(iargc, argv, options);
184 
185  // help requested? print full help text...
186  if (cmdline.optset(0))
187  {
188  cerr << usage_text << endl;
189  cerr << help_text << endl;
190  exit(0);
191  }
192 
193  /*----------------------------------------------------------------------*/
194  // extract command line options
195  //
196  Options opt;
197  opt.verbose=cmdline.optset(1);
198  opt.overwrite=cmdline.optset(2);
199  opt.demean=cmdline.optset(3);
200  opt.ndemean=cmdline.int_arg(3);
201  opt.cosinefrac=cmdline.double_arg(4);
202  opt.writetransform=cmdline.optset(5);
203  opt.transformfile=cmdline.string_arg(5);
204  opt.damping=cmdline.double_arg(6);
205  opt.inputformat=cmdline.string_arg(7);
206  opt.detrend=cmdline.optset(8);
207  opt.ndetrend=cmdline.int_arg(8);
208  opt.datetolerance=cmdline.double_arg(9);
209 
210  // check option values
211  TFXX_assert(((0<=opt.damping) && (opt.damping<=1.0)),
212  "Damping fraction is out of meaningful range");
213 
214  if (opt.verbose)
215  {
216  cout << DECONV_VERSION << endl;
217  }
218 
219  /*----------------------------------------------------------------------*/
220  // extract file names and trace numbers from command line
221  //
222  TFXX_assert(cmdline.extra(), "missing output file name");
223  std::string impulseresponsefilename=cmdline.next();
224 
225  TFXX_assert(cmdline.extra(), "missing input file name");
226  // data file parameters from command line
227  tfxx::cmdline::Tparsed
228  filenames=tfxx::cmdline::parse_cmdline(cmdline, keys);
229 
230  // extract input and ouput trace info
231  std::string filterinputname, filteroutputname;
232  int filterinputtrace, filteroutputtrace;
233  {
234  tfxx::cmdline::Tparsed::const_iterator I=filenames.begin();
235  TFXX_assert(I!=filenames.end(), "Missing filter input file name");
236  std::string tracelist="1";
237  if (I->haskey(tracekey)) { tracelist=I->value(tracekey); }
238  tfxx::RangeListStepper<int> rls(tfxx::string::rangelist<int>(tracelist));
239  TFXX_assert(rls.valid(), "Illegal tracelist");
240  filterinputname=I->name;
241  filterinputtrace=rls.current();
242  ++rls;
243  if (!rls.valid())
244  {
245  ++I;
246  TFXX_assert(I!=filenames.end(), "Missing filter output file name");
247  tracelist="1";
248  if (I->haskey(tracekey)) { tracelist=I->value(tracekey); }
249  rls=tfxx::RangeListStepper<int>(tfxx::string::rangelist<int>(tracelist));
250  TFXX_assert(rls.valid(), "Illegal tracelist");
251  }
252  filteroutputname=I->name;
253  filteroutputtrace=rls.current();
254  }
255  if (opt.verbose) {
256  cout << "filter input from file " << filterinputname
257  << " (trace #" << filterinputtrace << ")" << endl;
258  cout << "filter output from file " << filteroutputname
259  << " (trace #" << filteroutputtrace << ")" << endl;
260  }
261 
262  /*----------------------------------------------------------------------*/
263  // FREE block ro report processing
264  sff::FREE processfree, tracefree;
265  std::ostringstream freeline;
266 
267  processfree.append(DECONV_VERSION);
268 
269  /*----------------------------------------------------------------------*/
270  // read input time series
271 
272  sff::FREE finputfilefree, foutputfilefree;
273  Ttimeseries finputtseries, foutputtseries;
274 
275  {
276  // open filter input file
277  if (opt.verbose) { cout << "open input file " << filterinputname << endl; }
278  std::ifstream ifs(filterinputname.c_str());
279  datrw::ianystream is(ifs, opt.inputformat);
280  is >> finputfilefree;
281  int itrace=1;
282  while (itrace!=filterinputtrace) { is.skipseries(); ++itrace; }
283  TFXX_assert(is.good(), "Illegal trace for filter input");
284  is >> finputtseries;
285  }
286  tracefree.append("**** filter input signal");
287  processfree.append("filter input signal");
288  freeline << filterinputname << "(trace " << filterinputtrace << ")";
289  tracefree.append(freeline.str());
290  processfree.append(freeline.str());
291  processfree.append(finputfilefree);
292  processfree.append(finputtseries.header.free());
293 
294  {
295  // open filter output file
296  if (opt.verbose) { cout << "open input file " << filteroutputname << endl; }
297  std::ifstream ifs(filteroutputname.c_str());
298  datrw::ianystream is(ifs, opt.inputformat);
299  is >> foutputfilefree;
300  int itrace=1;
301  while (itrace!=filteroutputtrace) { is.skipseries(); ++itrace; }
302  TFXX_assert(is.good(), "Illegal trace for filter output");
303  is >> foutputtseries;
304  }
305  tracefree.append("**** filter output signal");
306  processfree.append("filter output signal");
307  freeline.str("");
308  freeline << filteroutputname << "(trace " << filteroutputtrace << ")";
309  tracefree.append(freeline.str());
310  processfree.append(freeline.str());
311  processfree.append(foutputfilefree);
312  processfree.append(foutputtseries.header.free());
313 
314  /*----------------------------------------------------------------------*/
315  // check header consistency
316  sff::WID2compare compare(sff::Fnsamples | sff::Fdt | sff::Fdate);
317  compare.setdatetolerance(opt.datetolerance);
318  if (opt.verbose) { cout << "checking consistency..." << endl; }
319  if (!compare (finputtseries.header.wid2(),foutputtseries.header.wid2()))
320  {
321  cerr << "ERROR: header signature mismatch:" << endl;
322  cerr << "filter input:" << endl;
323  cerr << finputtseries.header.wid2().line();
324  cerr << "filter output:" << endl;
325  cerr << foutputtseries.header.wid2().line();
326  TFXX_abort("baling out...");
327  }
328 
329  /*----------------------------------------------------------------------*/
330  // go for processing
331  processfree.append("==== start processing ====");
332 
333  // create taper
334  ts::tapers::Cosine taper(opt.cosinefrac);
335  freeline.str("");
336  freeline << "cosine taper fraction: " << opt.cosinefrac;
337  processfree.append(freeline.str());
338  if (opt.verbose) { cout << freeline.str() << endl; }
339 
340  const sff::WID2& iwid2=finputtseries.header.wid2();
341  const sff::WID2& owid2=foutputtseries.header.wid2();
342 
343  // demean
344  if (opt.demean) {
345  freeline.str("");
346  freeline << "remove average estimated for ";
347  if (opt.ndemean==0) { freeline << "all"; } else { freeline << opt.ndemean; }
348  freeline << " samples";
349  if (opt.verbose) { cout << freeline.str() << endl; }
350  processfree.append(freeline.str());
351  ts::filter::RemoveAverage demean(opt.ndemean);
352  demean(ts::filter::Ttimeseries(finputtseries, iwid2.dt));
353  demean(ts::filter::Ttimeseries(foutputtseries, owid2.dt));
354  }
355 
356  // detrend
357  if (opt.detrend) {
358  freeline.str("");
359  freeline << "remove trend estimated for ";
360  if (opt.ndetrend==0) { freeline << "all"; }
361  else { freeline << opt.ndetrend; }
362  freeline << " samples";
363  if (opt.verbose) { cout << freeline.str() << endl; }
364  processfree.append(freeline.str());
365  ts::filter::RemoveTrend detrend(opt.ndetrend);
366  detrend(ts::filter::Ttimeseries(finputtseries, iwid2.dt));
367  detrend(ts::filter::Ttimeseries(foutputtseries, owid2.dt));
368  }
369 
370  // apply taper
371  taper.apply(finputtseries);
372  taper.apply(foutputtseries);
373 
374  // create FFTW processor
375  Tfft fft;
376 
377  // Fourier transform
378  if (opt.verbose) { cout << "Fourier transformation..." << endl; }
379  Tfft::Tspectrum icoeff=fft(finputtseries, iwid2.dt);
380  Tfft::Tspectrum ocoeff=fft(foutputtseries, owid2.dt);
381 
382  // calculate energy in input signal
383  if (opt.verbose) { cout << "calculate energy in input signal..." << endl; }
384  double energy=0;
385  {
386  aff::Browser<Tfft::Tspectrum> IC(icoeff);
387  while (IC.valid())
388  {
389  energy += IC->real()*IC->real()+IC->imag()*IC->imag();
390  ++IC;
391  }
392  }
393  energy /= iwid2.nsamples;
394  double damping=energy*opt.damping;
395 
396  // calculate response function
397  // phase is calculated in terms of signal delay
398  Tfft::Tspectrum resp(icoeff.size());
399  Tseries amp(icoeff.size());
400  Tseries ampdirect(icoeff.size());
401  Tseries phase(icoeff.size());
402 
403  {
404  aff::Iterator<Tfft::Tspectrum> RC(resp);
405  aff::Browser<Tfft::Tspectrum> IC(icoeff);
406  aff::Browser<Tfft::Tspectrum> OC(ocoeff);
407  aff::Iterator<Tseries> AI(amp);
408  aff::Iterator<Tseries> ADI(ampdirect);
409  aff::Iterator<Tseries> PI(phase);
410  const double radtodegrees=180./3.1415926535897931159979634685441;
411 
412  while(IC.valid() && OC.valid() && RC.valid())
413  {
414  //*RC = ( (*OC) * std::conj(*IC) ) / ( (*IC) * std::conj(*IC) + damping );
415  *RC = ( (*OC) * std::conj(*IC) ) / ( std::norm(*IC) + damping );
416  if (AI.valid()) { *AI = std::abs(*RC); ++AI; }
417  if (PI.valid()) { *PI = -atan2(RC->imag(),RC->real())*radtodegrees; ++PI; }
418  //if (ADI.valid()) { *ADI = aoutput/ainput; ++ADI; }
419  if (ADI.valid()) { *ADI = (std::abs(*OC)/std::abs(*IC)); ++ADI; }
420  ++OC; ++IC; ++RC;
421  }
422  }
423 
424  // calculate impulse response
425  Tseries impresp=fft(resp, iwid2.dt);
426 
427  // write impulse response to output files
428  {
429  // open output file
430  // ----------------
431  std::string& outfile=impulseresponsefilename;
432  if (opt.verbose) { cout << "open output file " << outfile << endl; }
433  // check if output file exists and open
434  if (!opt.overwrite)
435  {
436  std::ifstream file(outfile.c_str(),std::ios_base::in);
437  TFXX_assert((!file.good()),"ERROR: output file exists!");
438  }
439  std::ofstream ofs(outfile.c_str());
440  datrw::osffstream os(ofs);
441 
442  os << iwid2;
443  os << impresp;
444  os << finputtseries;
445  os << foutputtseries;
446  }
447 
448  // write response spectrum
449 
450  if (opt.writetransform)
451  {
452  // length of time series
453  double T=iwid2.dt*iwid2.nsamples;
454  // frequency sampling interval
455  double df=1/T;
456  std::ofstream os(opt.transformfile.c_str());
457 
458  int fidx=0;
459  aff::Browser<Tseries> AI(amp);
460  aff::Browser<Tseries> ADI(ampdirect);
461  aff::Browser<Tseries> PI(phase);
462  aff::Browser<Tfft::Tspectrum> IC(icoeff);
463  aff::Browser<Tfft::Tspectrum> OC(ocoeff);
464 
465  while(AI.valid() && PI.valid() && ADI.valid() && IC.valid() && OC.valid())
466  {
467  double f=fidx*df;
468  os << f << " " << *AI << " " << *PI << " " << *ADI << " ";
469  os << " " << abs(*IC) << " " << abs(*OC);
470  os << endl;
471  ++AI; ++PI; ++fidx; ++ADI; ++IC; ++OC;
472  }
473  }
474 }
475 
476 /* ----- END OF deconv.cc ----- */
bool demean
Definition: deconv.cc:83
ts::sff::SFFTimeSeries< Tseries > Ttimeseries
Definition: deconv.cc:62
int ndemean
Definition: deconv.cc:84
std::string inputformat
Definition: cross.cc:75
double damping
Definition: deconv.cc:81
aff::Series< double > Tseries
Definition: deconv.cc:61
const char * keys[]
list of keys for filename specific parameters
Definition: deconv.cc:70
int main(int iargc, char *argv[])
Definition: deconv.cc:89
bool overwrite
Definition: autocorr.cc:61
fourier::fft::DRFFTWAFF Tfft
Definition: deconv.cc:63
bool verbose
Definition: autocorr.cc:61
const char *const tracekey
key to select traces
Definition: deconv.cc:68
double cosinefrac
Definition: deconv.cc:81
#define DECONV_VERSION
Definition: deconv.cc:36
std::string transformfile
Definition: deconv.cc:82
int ndetrend
Definition: deconv.cc:84
aff::Series< double > Tseries
Definition: cross.cc:69
bool detrend
Definition: deconv.cc:83
bool writetransform
Definition: deconv.cc:80
double datetolerance
Definition: deconv.cc:81