MsPASS C++ API  2.4.4.dev113+gc53d735d5
Defines the C++ API for MsPASS
Loading...
Searching...
No Matches
amplitudes.h
1#ifndef _AMPLITUDES_H_
2#define _AMPLITUDES_H_
3#include "mspass/algorithms/TimeWindow.h"
4#include "mspass/algorithms/algorithms.h"
5#include "mspass/seismic/Ensemble.h"
6#include "mspass/seismic/PowerSpectrum.h"
7#include "mspass/seismic/Seismogram.h"
8#include "mspass/seismic/TimeSeries.h"
9#include "mspass/utility/Metadata.h"
10#include "mspass/utility/MsPASSError.h"
11#include "mspass/utility/VectorStatistics.h"
12#include <algorithm>
13#include <cmath>
14#include <sstream>
15namespace mspass::algorithms::amplitudes {
16double PeakAmplitude(const mspass::seismic::CoreTimeSeries &d);
17double PeakAmplitude(const mspass::seismic::CoreSeismogram &d);
18double RMSAmplitude(const mspass::seismic::CoreTimeSeries &d);
19double RMSAmplitude(const mspass::seismic::CoreSeismogram &d);
20double PercAmplitude(const mspass::seismic::CoreTimeSeries &d,
21 const double perf);
22double PercAmplitude(const mspass::seismic::CoreSeismogram &d,
23 const double perf);
24double MADAmplitude(const mspass::seismic::CoreTimeSeries &d);
25double MADAmplitude(const mspass::seismic::CoreSeismogram &d);
26enum class ScalingMethod {
27 Peak,
28 RMS,
29 ClipPerc,
30 MAD
31};
32const std::string scale_factor_key("calib");
60template <typename Tdata>
61double scale(Tdata &d, const ScalingMethod method, const double level,
63 if ((method == ScalingMethod::ClipPerc) && (level <= 0.0 || level > 1.0))
65 "scale function: illegal perf level specified for clip percentage "
66 "scale - must be between 0 and 1\nData unaltered - may cause "
67 "downstream problems",
68 mspass::utility::ErrorSeverity::Suspect);
69 try {
70 double newcalib(1.0);
71 /* the else condition here should perhaps generate an elog message but
72 did not implement to allow this template to be used for CoreTimeSeries
73 and CoreSeismogram that do not have an elog attribute.*/
74 if (d.is_defined(scale_factor_key)) {
75 newcalib = d.get_double(scale_factor_key);
76 }
77 /* A reversed window is the public signal for full-record scaling.
78 Otherwise measure only the physical intersection with the datum. */
80 if (win.start > win.end) {
81 ampwindow.start = d.t0();
82 ampwindow.end = d.endtime();
83 } else {
84 ampwindow.start = std::max(win.start, d.t0());
85 ampwindow.end = std::min(win.end, d.endtime());
86 if (ampwindow.start > ampwindow.end) {
87 std::stringstream ss;
88 ss << "scale: amplitude measurement window [" << win.start << ", "
89 << win.end << "] has no intersection with data "
90 << "range [" << d.t0() << ", " << d.endtime() << "]";
92 ss.str(), mspass::utility::ErrorSeverity::Invalid);
93 }
94 }
95 Tdata windowed_data;
96 windowed_data = mspass::algorithms::WindowData(d, ampwindow);
97 double amplitude, dscale;
98 switch (method) {
99 case ScalingMethod::Peak:
100 amplitude = PeakAmplitude(windowed_data);
101 break;
102 case ScalingMethod::ClipPerc:
103 amplitude = PercAmplitude(windowed_data, level);
104 break;
105 case ScalingMethod::MAD:
106 amplitude = MADAmplitude(windowed_data);
107 break;
108 case ScalingMethod::RMS:
109 default:
110 amplitude = RMSAmplitude(windowed_data);
111 };
112 /* needed to handle case with a vector of all 0s*/
113 if (amplitude > 0.0) {
114 dscale = level / amplitude;
115 newcalib /= dscale;
116 d *= dscale;
117 d.put(scale_factor_key, newcalib);
118 } else {
119 std::stringstream ss;
120 ss << "Data array is all 0s and cannot be scaled";
121 d.elog.log_error("scale", ss.str(),
122 mspass::utility::ErrorSeverity::Complaint);
123 /* This may not be necessary but it assures this value is always set on
124 return even if it means nothing*/
125 d.put(scale_factor_key, newcalib);
126 }
127 return amplitude;
128 } catch (...) {
129 throw;
130 };
131}
156template <typename Tdata>
157std::vector<double>
158scale_ensemble_members(mspass::seismic::Ensemble<Tdata> &d,
159 const ScalingMethod &method, const double level,
161 if ((method == ScalingMethod::ClipPerc) && (level <= 0.0 || level > 1.0))
163 "scale_ensemble_members function: illegal perf level specified for "
164 "clip percentage scale - must be between 0 and 1\nData unaltered - may "
165 "cause downstream problems",
166 mspass::utility::ErrorSeverity::Suspect);
167 try {
168 typename std::vector<Tdata>::iterator dptr;
169 std::vector<double> amps;
170 amps.reserve(d.member.size());
171 for (dptr = d.member.begin(); dptr != d.member.end(); ++dptr) {
172 double thisamp;
173 thisamp = scale(*dptr, method, level, win);
174 amps.push_back(thisamp);
175 }
176 return amps;
177 } catch (...) {
178 throw;
179 };
180}
201template <typename Tdata>
202double scale_ensemble(mspass::seismic::Ensemble<Tdata> &d,
203 const ScalingMethod &method, const double level,
204 const bool use_mean) {
205 if ((method == ScalingMethod::ClipPerc) && (level <= 0.0 || level > 1.0))
207 "scale_ensemble function: illegal perf level specified for clip "
208 "percentage scale - must be between 0 and 1\nData unaltered - may "
209 "cause downstream problems",
210 mspass::utility::ErrorSeverity::Suspect);
211 try {
212 double avgamp; // defined here because the value computed here is returned
213 // on success
214 typename std::vector<Tdata>::iterator dptr;
215 std::vector<double> amps;
216 amps.reserve(d.member.size());
217 for (dptr = d.member.begin(); dptr != d.member.end(); ++dptr) {
218 double amplitude;
219 if (dptr->dead())
220 continue;
221 switch (method) {
222 case ScalingMethod::Peak:
223 amplitude = PeakAmplitude(*dptr);
224 break;
225 case ScalingMethod::ClipPerc:
226 amplitude = PercAmplitude(*dptr, level);
227 break;
228 case ScalingMethod::MAD:
229 amplitude = MADAmplitude(*dptr);
230 break;
231 case ScalingMethod::RMS:
232 default:
233 amplitude = RMSAmplitude(*dptr);
234 };
235 if ((amplitude > 0.0) && std::isfinite(amplitude))
236 amps.push_back(std::log(amplitude));
237 }
238 /* Silently return 0 without mutation when no member can define a gain. */
239 if (amps.empty())
240 return 0.0;
241 if (amps.size() == 1) {
242 avgamp = amps.front();
243 } else {
245 if (use_mean) {
246 avgamp = ampstats.mean();
247 } else {
248 avgamp = ampstats.median();
249 }
250 }
251
252 /* restore to a value instead of natural log*/
253 avgamp = std::exp(avgamp);
254 /* A core ensemble has no error-log channel, so a nonpositive result is a
255 silent no-op consistent with the no-eligible-member case above. */
256 if (avgamp <= 0.0) {
257 return 0.0;
258 }
259 double dscale = level / avgamp;
260 /* Now scale the data and apply calib */
261 for (dptr = d.member.begin(); dptr != d.member.end(); ++dptr) {
262 if (dptr->live()) {
263 double calib;
264 (*dptr) *= dscale;
265 if (dptr->is_defined(scale_factor_key)) {
266 calib = dptr->get_double(scale_factor_key);
267 } else {
268 calib = 1.0;
269 }
270 calib /= dscale;
271 dptr->put(scale_factor_key, calib);
272 }
273 }
274 return avgamp;
275 } catch (...) {
276 throw;
277 };
278}
280template <class T> std::vector<T> normalize(const std::vector<T> &d) {
281 size_t N = d.size();
282 std::vector<T> result;
283 result.reserve(N);
284 double d_nrm(0.0);
285 ;
286 for (size_t i = 0; i < N; ++i) {
287 d_nrm += (d[i] * d[i]);
288 result.push_back(d[i]);
289 }
290 d_nrm = sqrt(d_nrm);
291 for (size_t i = 0; i < N; ++i)
292 result[i] /= d_nrm;
293 return result;
294}
305public:
315 double f_range;
316 BandwidthData() {
317 low_edge_f = 0.0;
318 high_edge_f = 0.0;
319 low_edge_snr = 0.0;
320 high_edge_snr = 0.0;
321 f_range = 0.0;
322 };
325 double bandwidth_fraction() const {
326 if (f_range <= 0.0)
327 return 0.0;
328 else
329 return (high_edge_f - low_edge_f) / f_range;
330 };
332 double bandwidth() const {
333 /* All these conditionals are necessary for handling unexpected
334 * values. 0 is effectively and error return. Without these
335 * the function can return NaN or generate floating point exceptions. */
336 if ((f_range <= 0.0) || (high_edge_f <= low_edge_f) ||
337 (high_edge_f < 0.0) || (low_edge_f <= 0.0))
338 return 0.0;
339 else {
340 double ratio = high_edge_f / low_edge_f;
341 return 20.0 * log10(ratio);
342 }
343 };
344};
400BandwidthData EstimateBandwidth(const double signal_df,
403 const double snr_threshold, const double tbp,
404 const double fhs = -1.0,
405 const bool fix_high_edge_to_fhs = false);
445BandwidthStatistics(const mspass::seismic::PowerSpectrum &s,
447 const BandwidthData &bwd);
448} // namespace mspass::algorithms::amplitudes
449#endif
Defines a time window.
Definition TimeWindow.h:12
double start
Definition TimeWindow.h:17
double end
Definition TimeWindow.h:21
Holds parameters defining a passband computed from snr.
Definition amplitudes.h:304
double low_edge_f
Definition amplitudes.h:307
double bandwidth() const
Definition amplitudes.h:332
double bandwidth_fraction() const
Definition amplitudes.h:325
double high_edge_f
Definition amplitudes.h:309
double f_range
Definition amplitudes.h:315
double low_edge_snr
Definition amplitudes.h:311
double high_edge_snr
Definition amplitudes.h:313
Vector (three-component) seismogram data object.
Definition CoreSeismogram.h:39
Scalar time series data object.
Definition CoreTimeSeries.h:17
Metadata-bearing container for a collection of seismic data objects.
Definition Ensemble.h:14
std::vector< Tdata > member
Container holding data objects.
Definition Ensemble.h:25
Definition PowerSpectrum.h:11
Type-safe metadata container used throughout MsPASS.
Definition Metadata.h:101
Base class for error object thrown by MsPASS library routines.
Definition MsPASSError.h:38
Generic object to compute common robust statistics from a vector container of data.
Definition VectorStatistics.h:16