Waveform filter programs
fregra.cc
Go to the documentation of this file.
1 
36 #define FREGRA_VERSION \
37  "FREGRA V1.1 Spectrogram for unevenly sampled frequencies"
38 
39 #include <iostream>
40 #include <complex>
41 #include <tfxx/commandline.h>
42 #include <aff/series.h>
43 #include <aff/seriesoperators.h>
44 #include <tsxx/tsxx.h>
45 #include <tsxx/tapers.h>
46 #include <fstream>
47 #include <tfxx/error.h>
48 #include <tfxx/rangestring.h>
49 #include <tfxx/xcmdline.h>
50 #include <tfxx/misc.h>
51 #include <datrwxx/readany.h>
52 #include <datrwxx/writeany.h>
53 #include <sstream>
54 
55 using std::cout;
56 using std::cerr;
57 using std::endl;
58 
59 struct Options {
61  std::string inputformat, outputformat;
62  double fmin, fmax, olap, width;
63  int nfreq;
64 }; // struct Options
65 
66 // values type to be used for samples
67 typedef double Tvalue;
68 
69 // time series
70 typedef aff::Series<Tvalue> Tseries;
71 
72 /*======================================================================*/
73 
74 int main(int iargc, char* argv[])
75 {
76 
77  // define usage information
78  char usage_text[]=
79  {
80  FREGRA_VERSION "\n"
81  "usage: fregra [-v] [-o] [-type type] [-Type type] [-D] [-boxcar]" "\n"
82  " [-fmin f] [-fmax f] [-n n] [-olap f]\n"
83  " [-width f] [-log]\n"
84  " outpattern infile [t:T] [infile [t:T] ...]" "\n"
85  " or: fregra --help|-h" "\n"
86  " or: fregra --xhelp" "\n"
87  "\n"
88  "NOTICE: fregra is not yet fully implemented!"
89  };
90 
91  // define full help text
92  char help_text[]=
93  {
94  "outpattern name pattern for output file (may contain %N, which\n"
95  " will be replaced by sequential number).\n"
96  "infile input file (traces can be selected by the a list\n"
97  " definition like 3,5,8-12)\n"
98  "\n"
99  "-v be verbose\n"
100  "-o overwrite existing output file\n"
101  "-type type select input file type\n"
102  "-Type type select output file type\n"
103  "-DEBUG produce debug output\n"
104  "-boxcar use boxcar window (default: Hanning)\n"
105  "-fmin f smallest frequency to be used\n"
106  "-fmax f largest frequency to be used\n"
107  "-n n number of frequencies to be used\n"
108  "-olap f overlap of time windows\n"
109  "-width f width of frequency resolution\n"
110  "-log use logarithmic frequency scale\n"
111  };
112 
113  // define commandline options
114  using namespace tfxx::cmdline;
115  static Declare options[]=
116  {
117  // 0: print help
118  {"help",arg_no,"-"},
119  // 1: verbose mode
120  {"v",arg_no,"-"},
121  // 2: overwrite mode
122  {"o",arg_no,"-"},
123  // 3: debug mode
124  {"DEBUG",arg_no,"-"},
125  // 4: boxcar window
126  {"boxcar",arg_no,"-"},
127  // 5: smallest frequency
128  {"fmin",arg_yes,"0.1"},
129  // 6: largest frequency
130  {"fmax",arg_yes,"1."},
131  // 7: number of frequencies
132  {"n",arg_yes,"10"},
133  // 8: time window overlap
134  {"olap",arg_yes,"0.5"},
135  // 9: frequency width factor
136  {"width",arg_yes,"2."},
137  // 10: logarithmic frequency scale
138  {"log",arg_no,"-"},
139  // 11: input file type
140  {"type",arg_yes,"sff"},
141  // 12: output file type
142  {"Type",arg_yes,"sff"},
143  // 13: detailed help
144  {"xhelp",arg_no,"-"},
145  {NULL}
146  };
147 
148  static const char tracekey[]="t";
149 
150  // define commandline argument modifier keys
151  static const char* cmdlinekeys[]={tracekey, 0};
152 
153  // no arguments? print usage...
154  if (iargc<2)
155  {
156  cerr << usage_text << endl;
157  exit(0);
158  }
159 
160  // collect options from commandline
161  Commandline cmdline(iargc, argv, options);
162 
163  // help requested? print full help text...
164  if (cmdline.optset(0) || cmdline.optset(13))
165  {
166  cerr << usage_text << endl;
167  cerr << help_text << endl;
168  datrw::supported_input_data_types(cerr);
169  datrw::supported_output_data_types(cerr);
170  if (cmdline.optset(13)) { datrw::online_help(cerr); }
171  exit(0);
172  }
173 
174  Options opt;
175  opt.verbose=cmdline.optset(1);
176  opt.overwrite=cmdline.optset(2);
177  opt.debug=cmdline.optset(3);
178  opt.boxcar=cmdline.optset(4);
179  opt.fmin=cmdline.double_arg(5);
180  opt.fmax=cmdline.double_arg(6);
181  opt.nfreq=cmdline.int_arg(7);
182  opt.olap=cmdline.double_arg(8);
183  opt.width=cmdline.double_arg(9);
184  opt.log=cmdline.optset(10);
185  opt.inputformat=cmdline.optset(11);
186  opt.outputformat=cmdline.optset(11);
187 
188  // check validity of command line parameters
189  TFXX_assert((opt.fmin > 0), "illegal frequency value");
190  TFXX_assert((opt.fmax > 0), "illegal frequency value");
191  TFXX_assert((opt.olap > 0) && (opt.olap<1.), "illegal overlap value");
192  TFXX_assert((opt.width > 0), "illegal width value");
193  TFXX_assert((opt.nfreq > 0), "illegal number of frequencies");
194 
195  if (opt.verbose)
196  { cout << FREGRA_VERSION << endl; }
197 
198  // extract commandline arguments
199  TFXX_assert(cmdline.extra(), "missing output file pattern");
200  std::string outpattern=cmdline.next();
201  TFXX_assert(cmdline.extra(), "missing input file");
202 
203  tfxx::cmdline::Tparsed arguments=parse_cmdline(cmdline, cmdlinekeys);
204  if ((arguments.size()>1) && opt.verbose)
205  {
206  cout << "NOTICE: only the first trace will be processed!" <<
207  endl;
208  }
209 
210  /*======================================================================*/
211  // create processing description
212  sff::FREE processfree;
213 
214  ts::tapers::Hanning hanningtaper;
215 
216  // set up frequencies
217  Tseries frequencies(0,opt.nfreq-1);
218  if (opt.log)
219  {
220  double lfmin=std::log10(opt.fmin);
221  double lfmax=std::log10(opt.fmax);
222  double ldf=(lfmax-lfmin)/(static_cast<double>(opt.nfreq)-1);
223  for (int i=0; i<opt.nfreq; ++i)
224  {
225  frequencies(i)=std::pow(10.,lfmin+i*ldf);
226  }
227  } // if (opt.log)
228  else
229  {
230  double df=(opt.fmax-opt.fmin)/(static_cast<double>(opt.nfreq)-1);
231  for (int i=0; i<opt.nfreq; ++i)
232  {
233  frequencies(i)=opt.fmin+i*df;
234  }
235  } // if (opt.log) else
236 
237  /*======================================================================*/
238  // process one trace (currently the only one to be processed)
239 
240  unsigned int otrace=0;
241  tfxx::cmdline::Tparsed::const_iterator infile=arguments.begin();
242  while (infile != arguments.end())
243  {
244 
245  // open input file
246  if (opt.verbose) { cout << "open input file " << infile->name << endl; }
247  std::ifstream ifs(infile->name.c_str(),
248  datrw::ianystream::openmode(opt.inputformat));
249  datrw::ianystream is(ifs, opt.inputformat);
250 
251  // work on filoe header
252  sff::FREE infilefree;
253  if (is.hasfree()) { is >> infilefree; }
254  sff::SRCE insrceline;
255  if (is.hassrce()) { is >> insrceline; }
256 
257  // cycle through traces of input file
258  // ----------------------------------
259  // setup trace selection
260  unsigned int itrace=0;
261  typedef tfxx::RangeList<int> Trangelist;
262  bool doselect=infile->haskey(tracekey);
263  Trangelist traceranges=
264  tfxx::string::rangelist<Trangelist::Tvalue>(infile->value(tracekey));
265  while (is.good())
266  {
267  ++itrace;
268  if ((!doselect) || traceranges.contains(itrace))
269  {
270  ++otrace;
271 
272  TFXX_debug(opt.debug, "main", "process trace #" << itrace );
273 
274  if (opt.verbose)
275  { std::cout << " process trace #" << itrace << std::endl; }
276 
277  Tseries inputseries;
278  is >> inputseries;
279 
280  sff::WID2 wid2;
281  is >> wid2;
282 
283  ::sff::SRCE insrce;
284  is >> insrce;
285 
286  ::sff::FREE inputtracefree;
287  is >> inputtracefree;
288 
289  // a little helper
290  std::ostringstream oss;
291 
292  // open output file
293  // ----------------
294  std::string outfile=outpattern;
295  {
296  oss.str("");
297  oss << otrace;
298  outfile=tfxx::string::patsubst(outfile, "%N",
299  tfxx::string::trimws(oss.str()));
300  }
301  if (opt.verbose) { cout << "open output file " << outfile << endl; }
302  // check if output file exists and open
303  if (!opt.overwrite)
304  {
305  std::ifstream file(outfile.c_str(),std::ios_base::in);
306  TFXX_assert((!file.good()),"ERROR: output file exists!");
307  }
308  std::ofstream ofs(outfile.c_str(),
309  datrw::oanystream::openmode(opt.outputformat));
310  datrw::oanystream os(ofs, opt.outputformat);
311 
312  // prepare and write file header
313  ::sff::FREE outfilefree;
314  oss.str("");
315  oss << "trace #" << itrace << " from file " << infile->name;
316  outfilefree.append(oss.str());
317  outfilefree.append(processfree);
318  os << outfilefree;
319  ::sff::SRCE outsrce=insrce;
320  outsrce.cx=0.;
321  outsrce.cy=0.;
322  outsrce.cz=0.;
323  os << outsrce;
324 
325  // process all frequencies
326  double df;
327  for (int ifreq=0; ifreq<opt.nfreq; ++ifreq)
328  {
329  /*----------------------------------------------------------------------*/
330  // actual processing for current trace
331 
332  const double pi=3.141592653589793115998;
333  const double pi2=2.*pi;
334 
335  // define processing parameters
336  if (ifreq<opt.nfreq-1)
337  {
338  df=frequencies(ifreq+1)-frequencies(ifreq);
339  }
340 
341  double dt=wid2.dt;
342  double fwidth=df*opt.width;
343  double twin=1./(pi2*fwidth);
344  int nwin=static_cast<int>(twin/(dt));
345  TFXX_assert(nwin>0, "ERROR: invalid value!");
346  int nstep=static_cast<int>(twin/(dt*opt.olap));
347  if (nstep<1) { nstep=1; }
348 
349  // prepare output trace free
350  ::sff::FREE tracefree;
351  oss.str("");
352  oss << "processing frequency #" << ifreq
353  << " " << frequencies(ifreq) << " Hz";
354  tracefree.append(oss.str());
355  oss.str("");
356  oss << "frequency resolution: " << fwidth << " Hz";
357  tracefree.append(oss.str());
358  oss.str("");
359  oss << "Fourier transform window size: "
360  << nwin << " samples, "
361  << nwin*dt << " seconds";
362  tracefree.append(oss.str());
363  oss.str("");
364  oss << "Fourier transform window shifting: "
365  << nstep << " samples";
366  tracefree.append(oss.str());
367  tracefree.append(inputtracefree);
368 
369  // prepare output trace INFO line
370  ::sff::INFO outinfo;
371  outinfo.cs=sff::CS_cartesian;
372  outinfo.cx=frequencies(ifreq);
373 
374  Tseries fregram(inputseries.shape());
375  fregram=0.;
376 
377  typedef std::complex<double> Tcplx;
378  typedef aff::Series<Tcplx> Tcoeff;
379  unsigned int isize=static_cast<unsigned int>(2*nwin+1);
380  Tcplx ime(0.,1.);
381  Tcoeff ftfac(isize);
382  Tcplx ftexpfac=ime*pi2*frequencies(ifreq)*dt;
383  for (int i=ftfac.f(); i<=ftfac.l(); ++i)
384  { ftfac(i)=std::exp(ftexpfac*static_cast<double>(i)); }
385 
386  // taper and scaling for PSD
387  // scaling factor to adjust for taper effect
388  double tapscaling=1.;
389  if (opt.boxcar)
390  {
391  tapscaling=1.;
392  }
393  else
394  {
395  // scaling factor for Hanning
396  // see foutra.cc for derivation of this factor
397  tapscaling=8./3.;
398  hanningtaper.apply(ftfac);
399  }
400 
401  // we have an energy spectrum so far
402  // adjust scaling factor to obtain signal power
403  double scalingfactor=2.*tapscaling/(isize*dt);
404  ftfac *= scalingfactor;
405 
406  // deploy factors
407  Tseries depfac(0,2*nstep-1);
408  for (int i=0; i<2*nstep; ++i)
409  {
410  depfac(i)=-0.5*(std::cos(pi2*i/(2*nstep-1))-1.);
411  }
412 
413  // cycle through input series
414  int isample=inputseries.first();
415  while (isample <=inputseries.last())
416  {
417 
418  std::complex<double> C(0.,0.);
419  int imin=isample-nwin;
420  int imax=isample+nwin;
421  for (int i=imin; i<=imax; ++i)
422  {
423  if ((i>=inputseries.first()) && (i<=inputseries.last()))
424  {
425  // Fourier transform
426  C += ftfac(i-imin-ftfac.f())*inputseries(i);
427  } // if ((i>=inputseries.first()) && (i<=inputseries.last()))
428  } // for (int i=isample-nwin; i<isample+nwin; ++i)
429  double PSD=C.real()*C.real()+C.imag()*C.imag();
430 
431  // deploy results
432  int ibase=isample-nstep;
433  for (int i=0; i<2*nstep; ++i)
434  {
435  int j=ibase+i;
436  if ((j>=fregram.f())&&(j<=fregram.l()))
437  {
438  fregram(j)+=depfac(i)*PSD;
439  }
440  }
441 
442  isample+=nstep;
443  }
444 
445  os << fregram;
446  os << wid2;
447  os << outinfo;
448  os << tracefree;
449 
450  /*----------------------------------------------------------------------*/
451  } // while (Ifreq!=frequencies.end())^
452 
453  } // if ((!doselect) || traceranges.contains(itrace))
454  else
455  {
456  if (opt.verbose)
457  { std::cout << " skipping trace #" << itrace << std::endl; }
458  } // if ((!doselect) || traceranges.contains(itrace)) else
459  } // while (is.good())
460  ++infile;
461  } // while (infile != arguments.end())
462 
463 } // main
464 
465 /* ----- END OF fregra.cc ----- */
bool log
Definition: fregra.cc:60
std::string inputformat
Definition: cross.cc:75
double Tvalue
Definition: fregra.cc:67
static const char * cmdlinekeys[]
Definition: fidasexx.cc:131
#define FREGRA_VERSION
Definition: fregra.cc:36
bool overwrite
Definition: autocorr.cc:61
std::string outputformat
Definition: cross.cc:75
bool debug
Definition: autocorr.cc:61
int main(int iargc, char *argv[])
Definition: fregra.cc:74
int nfreq
Definition: fregra.cc:63
bool verbose
Definition: autocorr.cc:61
double olap
Definition: fregra.cc:62
double fmax
Definition: fregra.cc:62
const double pi
Definition: geophone.cc:99
double fmin
Definition: fregra.cc:62
bool boxcar
Definition: fregra.cc:60
const char *const tracekey
key to select traces
Definition: deconv.cc:68
double width
Definition: fregra.cc:62
aff::Series< Tvalue > Tseries
Definition: fregra.cc:70
aff::Series< double > Tseries
Definition: cross.cc:69