Waveform filter programs
foutra.cc
Go to the documentation of this file.
1 
72 #define FOUTRA_VERSION \
73  "FOUTRA V1.10 Fourier transforms"
74 
75 #include <iostream>
76 #include <tfxx/commandline.h>
77 #include <aff/series.h>
78 #include <aff/iterator.h>
79 #include <aff/dump.h>
80 #include <aff/seriesoperators.h>
81 #include <aff/functions/avg.h>
82 #include <aff/functions/rms.h>
83 #include <aff/functions/sqrsum.h>
84 #include <aff/subarray.h>
85 #include <tsxx/tsxx.h>
86 #include <tsxx/tapers.h>
87 #include <tsxx/filter.h>
88 #include <tsxx/wid2timeseries.h>
89 #include <tsioxx/tsioxx.h>
90 #include <fstream>
91 #include <tfxx/error.h>
92 #include <tfxx/rangestring.h>
93 #include <tfxx/xcmdline.h>
94 #include <tfxx/misc.h>
95 #include <tfxx/handle.h>
96 #include <tfxx/seitosh.h>
97 #include <datrwxx/readany.h>
98 #include <datrwxx/writeany.h>
99 #include <fourier/fftwaff.h>
100 #include <sstream>
101 
103 #include "foutra_options_help.h"
104 #include "foutra_usage_help.h"
105 
106 using std::cout;
107 using std::cerr;
108 using std::endl;
109 
110 struct Options {
122 }; // struct Options
123 
124 // values type to be used for samples
125 typedef double Tvalue;
126 
127 // time series
128 typedef aff::Series<Tvalue> Tseries;
129 
130 // full featured time series file
131 typedef ts::sff::File<Tseries> Tfile;
132 
133 typedef ts::TDsfftimeseries Ttimeseries;
135 
136 typedef fourier::fft::DRFFTWAFF Tfft;
137 
138 /*======================================================================*/
139 
140 int main(int iargc, char* argv[])
141 {
142 
143  // define usage information
144  char version_text[]=
145  {
146  FOUTRA_VERSION "\n"
147  };
148 
149  // define commandline options
150  using namespace tfxx::cmdline;
151  static Declare options[]=
152  {
153  // 0: print help
154  {"help",arg_no,"-"},
155  // 1: verbose mode
156  {"v",arg_no,"-"},
157  // 2: overwrite mode
158  {"o",arg_no,"-"},
159  // 3: input format
160  {"type",arg_yes,"sff"},
161  // 4: debug mode
162  {"D",arg_no,"-"},
163  // 5: amplitude spectrum
164  {"amplitude",arg_no,"-"},
165  // 6: power spectrum
166  {"power",arg_no,"-"},
167  // 7: apply boxcar taper
168  {"boxcar",arg_no,"-"},
169  // 8: average over constant number of samples
170  {"avg",arg_opt,"21"},
171  // 9: average over constant relative bandwidth (in decades)
172  {"rbw",arg_opt,"0.167"},
173  // 10: remove average
174  {"demean",arg_opt,"0"},
175  // 11: remove trend
176  {"detrend",arg_opt,"0"},
177  // 12: scale to mean in relative bandwidth
178  {"scalerbw",arg_opt,"0.167"},
179  // 13: write result to ASCII files
180  {"ASCII",arg_opt,"spectrum"},
181  // 14: write result to ASCII files
182  {"logascii",arg_opt,"0.167"},
183  // 15: average ASCII output only
184  {"avgascii",arg_no,"-"},
185  // 16: divisor for number of samples
186  {"divisor",arg_opt,"100"},
187  // 17: report rms value
188  {"rms",arg_no,"-"},
189  // 18: report rms value
190  {"harmonic",arg_no,"-"},
191  // 19: pad time series
192  {"pad",arg_yes,"1"},
193  // 20: subdivide input series
194  {"nsegments",arg_yes,"1"},
195  // 21: extended help
196  {"xhelp",arg_no,"-"},
197  // 22: input format
198  {"Type",arg_yes,"sff"},
199  // 23: input format
200  {"derivative",arg_opt,"1"},
201  {NULL}
202  };
203 
204  static const char tracekey[]="t";
205 
206  // define commandline argument modifier keys
207  static const char* cmdlinekeys[]={tracekey, 0};
208 
209  // no arguments? print usage...
210  if (iargc<2)
211  {
212  cerr << version_text << endl;
213  cerr << foutra_options_brief_help << endl;
214  cerr << tfxx::seitosh::repository_reference << endl;
215  exit(0);
216  }
217 
218  // collect options from commandline
219  Commandline cmdline(iargc, argv, options);
220 
221  // help requested? print full help text...
222  if (cmdline.optset(0) || cmdline.optset(21))
223  {
224  cerr << version_text << endl;
225  cerr << foutra_options_brief_help << endl;
226  cerr << foutra_options_help << endl;
227  cerr << foutra_usage_help << endl;
228  datrw::supported_data_types(cerr);
229  if (cmdline.optset(21)) { datrw::online_help(cerr); }
230  cerr << endl << tfxx::seitosh::repository_reference << endl;
231  exit(0);
232  }
233 
234  Options opt;
235  opt.verbose=cmdline.optset(1);
236  opt.overwrite=cmdline.optset(2);
237  opt.inputformat=cmdline.string_arg(3);
238  opt.debug=cmdline.optset(4);
239  opt.amplitudespectrum=cmdline.optset(5);
240  if (!opt.amplitudespectrum)
241  { opt.powerspectrum=cmdline.optset(6); }
242  opt.boxcartaper=cmdline.optset(7);
243  opt.avgconstbw=cmdline.optset(8);
244  opt.avgsamples=cmdline.int_arg(8);
245  opt.avgrelbw=cmdline.optset(9);
246  opt.decades=cmdline.double_arg(9);
247  opt.demean=cmdline.optset(10);
248  opt.ndemean=cmdline.int_arg(10);
249  opt.detrend=cmdline.optset(11);
250  opt.ndetrend=cmdline.int_arg(11);
251  opt.scalerbw=cmdline.optset(12);
252  opt.scaledecades=cmdline.double_arg(12);
253  opt.asciiout=cmdline.optset(13);
254  opt.asciibase=cmdline.string_arg(13);
255  opt.logascii=cmdline.optset(14);
256  opt.asciidecades=cmdline.double_arg(14);
257  opt.avgasciionly=cmdline.optset(15);
258  opt.adjustdivisor=cmdline.optset(16);
259  opt.divisor=cmdline.int_arg(16);
260  opt.reportrms=cmdline.optset(17);
261  opt.harmonicsignal=cmdline.optset(18);
262  opt.padzeroes=cmdline.optset(19);
263  opt.padfactor=cmdline.int_arg(19);
264  opt.nsegments=cmdline.int_arg(20);
265  opt.outputformat=cmdline.string_arg(22);
266  opt.derivative=cmdline.optset(23);
267  opt.nderivative=cmdline.double_arg(23);
268 
269  TFXX_assert((opt.divisor > 0), "illegal value for argument divisor");
270  TFXX_assert((opt.nsegments > 0), "illegal value for argument nsegments");
271  TFXX_assert((opt.padfactor > 0), "illegal value for argument pad");
272 
273  if (opt.verbose)
274  { cout << FOUTRA_VERSION << endl; }
275 
276  // extract commandline arguments
277  TFXX_assert(cmdline.extra(), "missing output file");
278  std::string outfile=cmdline.next();
279  TFXX_assert(cmdline.extra(), "missing input file");
280  tfxx::cmdline::Tparsed arguments=parse_cmdline(cmdline, cmdlinekeys);
281  if ((arguments.size()>1) && opt.verbose)
282  {
283  cout << "NOTICE: file specific information (SRCE line and file FREE)" <<
284  endl
285  << " will be taken from first file only!" << endl;
286  }
287 
288  /*======================================================================*/
289  // do not apply segmentation if not PSD or harmonic signal analysis
290  if (!(opt.powerspectrum || opt.harmonicsignal)) { opt.nsegments=1; }
291 
292  /*======================================================================*/
293  // create processing description
294  sff::FREE processfree;
295  if (opt.nsegments>1)
296  {
297  std::ostringstream freeline;
298  freeline << "Input series is subdivided into "
299  << opt.nsegments << " segments with 50% overlap";
300  processfree.append(freeline.str());
301  processfree.append("The analysis is done individually for all segments");
302  processfree.append("The result is the average over all segments");
303  }
304  if (opt.demean)
305  {
306  std::ostringstream freeline;
307  freeline << "The average over ";
308  if (opt.ndemean >0)
309  {
310  freeline << opt.ndemean;
311  }
312  else
313  {
314  freeline << "all";
315  }
316  freeline << " samples is removed from all samples";
317  processfree.append(freeline.str());
318  }
319  if (opt.detrend)
320  {
321  std::ostringstream freeline;
322  freeline << "The trend over ";
323  if (opt.ndetrend >0)
324  {
325  freeline << opt.ndetrend;
326  }
327  else
328  {
329  freeline << "all";
330  }
331  freeline << " samples is removed from all samples";
332  processfree.append(freeline.str());
333  }
334  if (opt.boxcartaper)
335  {
336  processfree.append("A boxcar taper (i.e no taper) is applied to the data");
337  }
338  else
339  {
340  processfree.append("A Hanning taper is applied to the data");
341  }
342  if (opt.padzeroes)
343  {
344  std::ostringstream freeline;
345  freeline << "pad with zeroes increasing the total "
346  << "number of samples by a factor of " << opt.padfactor;
347  processfree.append(freeline.str());
348  }
349  if (opt.harmonicsignal || opt.amplitudespectrum || opt.powerspectrum)
350  {
351  processfree.append("An appropriately scaled FFT (libdrfftw) is applied");
352  if (opt.derivative)
353  {
354  processfree.append("Calculate values for derivative with respect to");
355  processfree.append(" time by multiplication of the Fourier coefficients");
356  std::ostringstream freeline;
357  freeline << " with the angular frequency to the power of "
358  << opt.nderivative;
359  processfree.append(freeline.str());
360  }
361  }
362  if (opt.amplitudespectrum)
363  {
364  processfree.append("The result are coefficients of the amplitude spectrum");
365  processfree.append("Units are: input units / Hz");
366  }
367  else if (opt.powerspectrum)
368  {
369  processfree.append("The result are coefficients of the power spectrum");
370  if (opt.scalerbw)
371  {
372  std::ostringstream freeline;
373  freeline << "The result is the average power in "
374  << opt.scaledecades << " decades";
375  processfree.append(freeline.str());
376  processfree.append("Units are: input units squared");
377  }
378  else
379  {
380  processfree.append("Units are: input units squared / Hz");
381  }
382  if ((opt.avgconstbw || opt.avgrelbw) && !opt.avgasciionly)
383  {
384  processfree.append("The spectrum is smoothed with a boxcar over");
385  if (opt.avgconstbw)
386  {
387  std::ostringstream freeline;
388  freeline << opt.avgsamples << " samples";
389  processfree.append(freeline.str());
390  }
391  else if (opt.avgrelbw)
392  {
393  std::ostringstream freeline;
394  freeline << opt.decades << " decades";
395  processfree.append(freeline.str());
396  }
397  }
398  else
399  {
400  processfree.append("The spectrum is not smoothed");
401  if (opt.avgasciionly && opt.asciiout)
402  {
403  processfree.append("The ASCII output is smoothed with a boxcar over");
404  std::ostringstream freeline;
405  freeline << opt.decades << " decades";
406  processfree.append(freeline.str());
407  }
408  }
409  }
410  else if (opt.harmonicsignal)
411  {
412  processfree.append("The input is understood to contain harmonic signals.");
413  processfree.append("The result are signal amplitudes for harmonic peaks.");
414  processfree.append("Units are: input units");
415  }
416  else
417  {
418  processfree.append("The output is a tapered version of the time series");
419  }
420  if (opt.amplitudespectrum || opt.powerspectrum || opt.harmonicsignal)
421  {
422  processfree.append("The sampling interval provided in the WID2 line");
423  processfree.append("specifies the sampling interval of the spectrum");
424  processfree.append("in Hz. The first coefficient is that at 0 Hz.");
425  }
426 
427  if (opt.verbose)
428  {
429  cout << "processing of each trace will take place as follows:" << endl;
430  for(sff::FREE::Tlines::const_iterator I=processfree.lines.begin();
431  I != processfree.lines.end(); ++I)
432  { cout << " " << *I << std::endl; }
433  }
434 
435  /*======================================================================*/
436  // start processing
437 
438  // create taper
439  ts::tapers::Hanning taper;
440  // create FFTW processor
441  Tfft fft;
442 
443  // open output file
444  // ----------------
445  if (opt.verbose) { cout << "open output file " << outfile << endl; }
446  // check if output file exists and open
447  if (!opt.overwrite)
448  {
449  std::ifstream file(outfile.c_str(),std::ios_base::in);
450  TFXX_assert((!file.good()),"ERROR: output file exists!");
451  }
452  std::ofstream ofs(outfile.c_str());
453  datrw::oanystream os(ofs, opt.outputformat);
454 
455  // prepare file FREE block
456  sff::FREE filefree;
457  filefree.append(FOUTRA_VERSION);
458  // set flag to process header of first input file
459  bool firstfile=true;
460  // count output traces
461  int otrace=0;
462  // cycle through all input files
463  // -----------------------------
464  tfxx::cmdline::Tparsed::const_iterator infile=arguments.begin();
465  while (infile != arguments.end())
466  {
467  // open input file
468  if (opt.verbose) { cout << "open input file " << infile->name << endl; }
469  std::ifstream ifs(infile->name.c_str());
470  datrw::ianystream is(ifs, opt.inputformat);
471  // handle file header
472  if (firstfile)
473  {
474  if (is.hasfree())
475  {
476  sff::FREE infilefree;
477  is >> infilefree;
478  filefree.append("block read from first input file:");
479  filefree.append(infilefree);
480  }
481  os << filefree;
482  if (is.hassrce())
483  {
484  sff::SRCE insrceline;
485  is >> insrceline;
486  os << insrceline;
487  }
488  }
489 
490  // cycle through traces of input file
491  // ----------------------------------
492  // setup trace selection
493  typedef tfxx::RangeList<int> Trangelist;
494  bool doselect=infile->haskey(tracekey);
495  Trangelist traceranges=
496  tfxx::string::rangelist<Trangelist::Tvalue>(infile->value(tracekey));
497  int itrace=0;
498  while (is.good())
499  {
500  ++itrace;
501  if ((!doselect) || traceranges.contains(itrace))
502  {
503  TFXX_debug(opt.debug, "main", "process trace #" << itrace );
504  if (opt.verbose)
505  { std::cout << " process trace #" << itrace << std::endl; }
506  Tseries inputseries;
507  is >> inputseries;
508  sff::WID2 wid2;
509  is >> wid2;
510  TFXX_debug(opt.debug, "main", " series and WID2 are read");
511 
512  /*----------------------------------------------------------------------*/
513  // report rms value if requested
514  if (opt.reportrms)
515  {
516  std::cout << " rms-value of input time series: "
517  << aff::func::rms(inputseries) << std::endl;
518  }
519  /*----------------------------------------------------------------------*/
520  // process data
521  //
522  // inputseries: full series read from file
523  // series: series for one segment to be analysed
524  // the spectral representation of a single segment is also stored in
525  // this container
526  // spectrum: resulting spectral representation
527  //
528  // in the previous (pre segmentation) version of foutra only "series" was
529  // used; we will make use of the aff::Series reference semantics to use the
530  // old code with no more modifications than necessary; such "series" will
531  // be used for different things
532  //
533  // wid2.dt remains valid throughout the processing
534  // wid2.nsamples is only correct for the inputseries
535  // use series.size() for other request for number of samples
536 
537  // prepare segmentation
538  // --------------------
539  int nsegsamples=inputseries.size();
540  if (opt.nsegments>1)
541  {
542  nsegsamples=2*inputseries.size()/(opt.nsegments+1);
543  }
544 
545  // adjust length of time series
546  if (opt.adjustdivisor)
547  {
548  if (opt.verbose)
549  {
550  std::cout << " adjust divisor to " << opt.divisor << std::endl;
551  std::cout << " number of input samples: " << wid2.nsamples
552  <<std::endl;
553  std::cout << " number of segment samples: " << nsegsamples
554  <<std::endl;
555  }
556  int rest=nsegsamples % opt.divisor;
557  nsegsamples=nsegsamples-rest;
558  if (opt.verbose)
559  {
560  std::cout << " number of processing samples: " << nsegsamples
561  <<std::endl;
562  }
563  }
564 
565  // segment stride
566  int segstride=inputseries.size()/(opt.nsegments+1);
567 
568  if (opt.verbose && (opt.nsegments>1))
569  {
570  cout << " number of input samples: "
571  << inputseries.size() << endl;
572  cout << " number of segments: "
573  << opt.nsegments << endl;
574  cout << " number of samples in each segment: "
575  << nsegsamples << endl;
576  cout << " stride between segments: "
577  << segstride << endl;
578  cout << " overlap of proximate segments: "
579  << 100.*(nsegsamples-segstride)/nsegsamples << "%" << endl;
580  }
581 
582  // length of time series segment
583  double T=wid2.dt*nsegsamples;
584  // length of taper window
585  double Tw=T;
586  // frequency sampling interval
587  double df=1./T;
588 
589  if (opt.padzeroes)
590  {
591  T *= opt.padfactor;
592  df /= opt.padfactor;
593  }
594 
595  if (opt.verbose)
596  {
597  cout << " duration T of each segment: "
598  << T << "s" << endl;
599  cout << " duration Tw of signal window: "
600  << Tw << "s" << endl;
601  cout << " frequency sampling interval df: "
602  << df << "Hz" << endl;
603  }
604 
605  // the result will be collected in
606  Tseries spectrum;
607 
608  // segment counter
609  int isegment=0;
610 
611  // start actual processing
612  // -----------------------
613  while (isegment<opt.nsegments)
614  {
615  int firstindex=inputseries.first()+isegment*segstride;
616  int lastindex=firstindex+nsegsamples-1;
617  TFXX_debug(opt.debug, "main", " segment index range: "
618  << firstindex << "-" << lastindex);
619  TFXX_assert((firstindex >= inputseries.first()) &&
620  (firstindex <= inputseries.last()) &&
621  (lastindex >= inputseries.first()) &&
622  (lastindex <= inputseries.last()),
623  "ERROR: index out of range; program design error");
624  Tseries segseries=aff::subarray(inputseries)(firstindex,lastindex);
625  Tseries series=segseries;
626  if (opt.nsegments>1) { series=segseries.copyout(); }
627  series.shift(-series.first());
628 
629  // demean
630  if (opt.demean) {
631  TFXX_debug(opt.debug, "main", " demean");
632  ts::filter::RemoveAverage demean(opt.ndemean);
633  demean(ts::filter::Ttimeseries(series, wid2.dt));
634  }
635 
636  // detrend
637  if (opt.detrend) {
638  TFXX_debug(opt.debug, "main", " detrend");
639  ts::filter::RemoveTrend detrend(opt.ndetrend);
640  detrend(ts::filter::Ttimeseries(series, wid2.dt));
641  }
642 
643  // apply taper
644  if (!opt.boxcartaper) {
645  TFXX_debug(opt.debug, "main", " apply taper");
646  taper.apply(series);
647  }
648 
649  // pad time series
650  // Notice: padding the signals requires correct scaling of amplitudes
651  // this is only implemented for harmonic signals up to now
652  if (opt.padzeroes)
653  {
654  Tseries newseries(series.size()*opt.padfactor);
655  newseries=0.;
656  Tseries subseries(aff::subarray(newseries)(series.f(),series.l()));
657  subseries.copyin(series);
658  series=newseries;
659  }
660 
661  // call FFT for amplitude or power spectrum or harmonic signal
662  if (opt.amplitudespectrum || opt.powerspectrum || opt.harmonicsignal)
663  {
664  TFXX_debug(opt.debug, "main", "call FFT");
665  Tfft::Tspectrum coeff=fft(series, wid2.dt);
666  TFXX_debug(opt.debug, "main",
667  "returned from FFT; create output series");
668  series=Tseries(coeff.size());
669  TFXX_debug(opt.debug, "main", "create iterators");
670  aff::Iterator<Tseries> S(series);
671  aff::Browser<Tfft::Tspectrum> C(coeff);
672 
673  // take derivative if selected
674  if (opt.derivative)
675  {
676  for (int i=coeff.f()+1; i<=coeff.l(); ++i)
677  {
678  double frequency=df*(i-coeff.f())*2*M_PI;
679  double thepower=opt.nderivative;
680  double factor=std::pow(frequency,thepower);
681  coeff(i) *= factor;
682  }
683  }
684 
685  TFXX_debug(opt.debug, "main", "calculate square of modulus and copy");
686  while(S.valid() && C.valid())
687  {
688  *S = C->real()*C->real()+C->imag()*C->imag();
689  ++S; ++C;
690  }
691  }
692 
693  // take sqrt for amplitude spectrum
694  if (opt.amplitudespectrum || opt.harmonicsignal)
695  {
696  TFXX_debug(opt.debug, "main", "calculate sqrt and scale");
697  aff::Iterator<Tseries> S(series);
698  while(S.valid())
699  {
700  *S = std::sqrt(*S);
701  ++S;
702  }
703  }
704 
705  // apply scaling appropriate for harmonic signals
706  if (opt.harmonicsignal)
707  {
708  // amplitude of Hanning taper instrument function equals 1
709  // amplitude of Boxcar taper instrument function equals 2
710  double fscaling;
711  if (opt.boxcartaper)
712  { fscaling = 2./Tw; }
713  else
714  { fscaling = 4./Tw; }
715  series *= fscaling;
716  }
717 
718  // apply appropriate scaling and averaging for power spectrum
719  if (opt.powerspectrum)
720  {
721  // scaling factor to adjust for taper effect
722  double tapscaling=1.;
723  if (opt.boxcartaper)
724  {
725  tapscaling=1.;
726  }
727  else
728  {
729  // scaling factor for Hanning
730  // see below for derivation of this factor
731  tapscaling=8./3.;
732  }
733 
734  // we have an energy spectrum so far
735  // adjust scaling factor to obtain signal power
736  //
737  // This factor computes the one-sided PSD, i.e. twice the Fourier
738  // transform of the normalized auto-correlation function.
739  double scalingfactor=2.*tapscaling/Tw;
740 
741  // scale to relative bandwidth if requested
742  if (opt.scalerbw)
743  {
744  TFXX_debug(opt.debug, "main",
745  "scale to average in relative bandwidth");
746  double bwfactor=(std::pow(10.,opt.scaledecades)-1.)/
747  std::pow(10.,opt.scaledecades/2.);
748  // cout << "bwfactor: " << bwfactor << endl;
749  // cout << "index range: "
750  // << series.f() << "-" << series.l() << endl;
751  for (int k=series.f(); k<=series.l(); ++k)
752  {
753  // center frequency
754  double fm=k*df;
755  series(k) *= scalingfactor*bwfactor*fm;
756  }
757  } // if (opt.scalerbw)
758  else
759  {
760  TFXX_debug(opt.debug, "main", "scale");
761  series *= scalingfactor;
762  } // if (opt.scalerbw) else
763 
764  // apply smoothing
765  if (opt.avgconstbw && !opt.avgasciionly)
766  {
767  TFXX_debug(opt.debug, "main", "average over constant bandwidth");
768  // create a copy
769  Tseries p(series.size());
770  p.copyin(series);
771  int d=opt.avgsamples/2+1;
772  for (int k=p.f(); k<=p.l(); ++k)
773  {
774  int k1=k-d;
775  int k2=k+d;
776  k1 = k1 < p.f() ? p.f() : k1;
777  k2 = k2 > p.l() ? p.l() : k2;
778  // cout << k1 << " " << k << " " << k2 << endl;
779  TFXX_assert(k2>k1, "negative bandwidth for averaging")
780  Tseries sub=aff::subarray(p)(k1,k2);
781  series(k)=aff::func::avg(sub);
782  /*
783  series(k)=0;
784  for (int j=sub.f(); j<=sub.l(); ++j)
785  {
786  series(k) += sub(j);
787  }
788  series(k) /= double(sub.size());
789  */
790  } // for (int k=p.f(); k<=p.l(); ++k)
791  } // if (opt.avgconstbw && !opt.avgasciionly)
792  else if (opt.avgrelbw && !opt.avgasciionly)
793  {
794  TFXX_debug(opt.debug, "main", "average over relative bandwidth");
795  // create a copy
796  TFXX_debug(opt.debug, "main", " create a copy");
797  Tseries p(series.size());
798  TFXX_debug(opt.debug, "main", " copy in data");
799  p.copyin(series);
800  double bwfactor=std::sqrt(std::pow(10.,opt.decades));
801  // cout << bwfactor << endl;
802  TFXX_debug(opt.debug, "main", " cycle over data");
803  for (int k=p.f(); k<=p.l(); ++k)
804  {
805  // center frequency
806  double fm=k*df;
807  double f1=fm/bwfactor;
808  double f2=fm*bwfactor;
809  // cout << fm << " " << f1 << " " << f2 << endl;
810  int k1=int(f1/df);
811  int k2=int(f2/df)+1;
812  k1 = k1 < p.f() ? p.f() : k1;
813  k2 = k2 > p.l() ? p.l() : k2;
814  TFXX_assert(k2>k1, "negative bandwidth for averaging")
815  Tseries sub=aff::subarray(p)(k1,k2);
816  series(k)=aff::func::avg(sub);
817  } // for (int k=p.f(); k<=p.l(); ++k)
818  } // else if (opt.avgrelbw && !opt.avgasciionly)
819  } // if (opt.powerspectrum)
820 
821  // collect result
822  if (isegment==0)
823  {
824  spectrum=series.copyout();
825  TFXX_debug(opt.debug, "main", "copy result");
826  }
827  else
828  {
829  // spectrum += series;
830  for (int i=spectrum.f(),
831  j=series.f();
832  i<=spectrum.l(); ++i, ++j)
833  {
834  TFXX_assert(j<=series.l(),
835  "ERROR: program design error");
836  spectrum(i) += series(j);
837  }
838  TFXX_debug(opt.debug, "main", "added result");
839  }
840 
841  ++isegment;
842  TFXX_debug(opt.debug, "main",
843  "finished segment #" << isegment);
844  } // while (isegment<opt.nsegments)
845 
846  if (opt.nsegments>1) { spectrum /= opt.nsegments; }
847  // use reference semantics to make results available under previous
848  // name
849  Tseries series=spectrum;
850 
851  /*----------------------------------------------------------------------*/
852  // end of analysis section
853  // begin of output section
854  /*----------------------------------------------------------------------*/
855 
856  // adjust sampling interval to frequency interval
857  if (opt.amplitudespectrum || opt.powerspectrum || opt.harmonicsignal)
858  { wid2.dt=df; }
859  wid2.nsamples=series.size();
860 
861  os << wid2;
862  TFXX_debug(opt.debug, "main", " series and WID are written");
863  if (is.hasinfo()) { sff::INFO info; is >> info; os << info; }
864  if (is.hasfree() || true)
865  {
866  sff::FREE tracefree;
867  is >> tracefree;
868  tracefree.append(FOUTRA_VERSION);
869  tracefree.append("data read from file " + infile->name);
870  tracefree.append(processfree);
871  os << tracefree;
872  }
873  os << series;
874 
875  // write ASCII output if requested
876  if (opt.asciiout)
877  {
878  TFXX_debug(opt.debug, "main", "write to ASCII file");
879  ++otrace;
880  std::ostringstream outname;
881  outname << opt.asciibase << ".";
882  outname.width(3);
883  outname.fill('0');
884  outname << otrace << ".asc";
885  std::string filename(outname.str());
886  std::ofstream aos(filename.c_str());
887 
888  Tseries f;
889  Tseries p;
890  if (opt.logascii)
891  {
892  TFXX_debug(opt.debug, "main",
893  "prepare data with logarithmic scale");
894  // f_k = f_min * pow(10,k*ndecades)
895  // k = log10( f_k/f_min ) / ndecades
896  int kmin=0;
897  // f_min = df
898  // f_max = df*series.l();
899  int kmax=int(std::log10(double(series.l()))/opt.asciidecades);
900  TFXX_debug(opt.debug, "main",
901  " output index range: " << kmin << "-" << kmax);
902  f=Tseries(kmin,kmax);
903  p=Tseries(kmin,kmax);
904  double bwfactor=std::sqrt(std::pow(10.,opt.decades));
905  for (int k=f.f(); k<=f.l(); ++k)
906  {
907  // center frequency
908  double fm=df*std::pow(10.,double(k*opt.asciidecades));
909  int l=int(0.5+fm/df);
910  f(k)=l*df;
911  if (opt.avgasciionly)
912  {
913  // center frequency
914  double fm=f(k);
915  double f1=fm/bwfactor;
916  double f2=fm*bwfactor;
917  int l1=int(f1/df);
918  int l2=int(f2/df)+1;
919  l1 = l1 < series.f() ? series.f() : l1;
920  l2 = l2 > series.l() ? series.l() : l2;
921  /*
922  TFXX_debug(opt.debug, "main",
923  " center frequency: " << fm << " Hz");
924  TFXX_debug(opt.debug, "main",
925  " average: " << f1 << " Hz - " << f2 << " Hz");
926  TFXX_debug(opt.debug, "main",
927  " index range: " << l1 << " - " << l2);
928  */
929  TFXX_assert(l2>=l1, "negative bandwidth for averaging");
930  Tseries sub=aff::subarray(series)(l1,l2);
931  p(k)=aff::func::avg(sub);
932  } // if (opt.avgasciionly)
933  else
934  {
935  p(k)=series(l);
936  } // if (opt.avgasciionly) else
937  } // for (int k=f.f(); k<=f.l(); ++k)
938  } // if (opt.logascii)
939  else
940  {
941  // just copy and prepare frequency vector
942  f=Tseries(series.size());
943  p=Tseries(series.size());
944  p.copyin(series);
945  for (int k=f.f(); k<=f.l(); ++k)
946  {
947  f(k) = k*df;
948  }
949  } // if (opt.logascii) else
950  for (int k=f.f(); k<=f.l(); ++k)
951  {
952  // center frequency
953  aos.setf(std::ios_base::scientific,std::ios_base::floatfield);
954  aos.precision(10);
955  aos << f(k) << " ";
956  aos.setf(std::ios_base::scientific,std::ios_base::floatfield);
957  aos.precision(10);
958  aos << p(k) << endl;
959  }
960  } // if (opt.asciiout)
961 
962  TFXX_debug(opt.debug, "main",
963  "trace #" << itrace << " successfully processed");
964  } // if ((!doselect) || traceranges.contains(itrace))
965  else
966  {
967  TFXX_debug(opt.debug, "main", "skip trace #" << itrace );
968  if (opt.verbose)
969  { std::cout << " skip trace #" << itrace << std::endl; }
970  is.skipseries();
971  } // if ((!doselect) || traceranges.contains(itrace)) else
972  } // while (is.good())
973 
974  // go to next file
975  firstfile=false;
976  ++infile;
977  } // while (infile != arguments.end())
978 
979 } // main
980 
981 /*======================================================================*/
982 
1018 /*----------------------------------------------------------------------*/
1019 
1098 /*----------------------------------------------------------------------*/
1099 
1267 /* ----- END OF foutra.cc ----- */
bool demean
Definition: deconv.cc:83
fourier::fft::DRFFTWAFF Tfft
Definition: foutra.cc:136
int nsegments
Definition: foutra.cc:121
bool padzeroes
Definition: foutra.cc:120
bool avgasciionly
Definition: foutra.cc:114
bool powerspectrum
Definition: foutra.cc:113
bool scalerbw
Definition: foutra.cc:115
char foutra_options_brief_help[]
int ndemean
Definition: deconv.cc:84
int padfactor
Definition: foutra.cc:121
bool logascii
Definition: foutra.cc:111
bool boxcartaper
Definition: foutra.cc:113
std::string inputformat
Definition: cross.cc:75
bool amplitudespectrum
Definition: foutra.cc:113
int main(int iargc, char *argv[])
Definition: foutra.cc:140
double asciidecades
Definition: foutra.cc:118
bool avgconstbw
Definition: foutra.cc:114
double decades
Definition: foutra.cc:118
char foutra_usage_help[]
char foutra_options_help[]
double Tvalue
Definition: foutra.cc:125
static const char * cmdlinekeys[]
Definition: fidasexx.cc:131
#define FOUTRA_VERSION
Definition: foutra.cc:72
bool overwrite
Definition: autocorr.cc:61
std::string outputformat
Definition: cross.cc:75
fourier::fft::DRFFTWAFF Tfft
Definition: deconv.cc:63
bool derivative
Definition: foutra.cc:116
std::string asciibase
Definition: foutra.cc:112
bool debug
Definition: autocorr.cc:61
ts::TDsfftimeseries Ttimeseries
Definition: foutra.cc:133
bool asciiout
Definition: foutra.cc:111
aff::Series< Tvalue > Tseries
Definition: foutra.cc:128
double nderivative
Definition: foutra.cc:118
bool verbose
Definition: autocorr.cc:61
bool avgrelbw
Definition: foutra.cc:114
const char *const tracekey
key to select traces
Definition: deconv.cc:68
double scaledecades
Definition: foutra.cc:118
int divisor
Definition: foutra.cc:117
int ndetrend
Definition: deconv.cc:84
int avgsamples
Definition: foutra.cc:119
bool adjustdivisor
Definition: foutra.cc:115
bool reportrms
Definition: foutra.cc:120
bool harmonicsignal
Definition: foutra.cc:120
aff::Series< double > Tseries
Definition: cross.cc:69
bool detrend
Definition: deconv.cc:83
ts::sff::File< Tseries > Tfile
Definition: foutra.cc:131