#include "gtl_scr_spec.h" #include "qvariant.h" #include namespace gtl { namespace scr { spec::spec(gtl::math::spec::types type, gtl::analog_data *ad) : gtl::math::spec(type, ad) , _name(ad->name()) , _color(ad->color()) , _is_visible(true) , _smoothing_factor(10) , _smoothed_line_color(ad->color()) , _peak_level(6) , _harm_tolerance(1) , _max_harm_level(0) { connect(this, >l::math::spec::initialized, this, >l::scr::spec::reset); } QString spec::name() const { return _name; } int spec::color() const { return _color; } bool spec::is_visible() const { return _is_visible; } int spec::smoothing_factor() const { return _smoothing_factor; } int spec::smoothed_line_color() const { return _smoothed_line_color; } qreal spec::peak_level() const { return _peak_level; } void spec::get_smoothed_line(std::back_insert_iterator > data) const { std::copy(_smoothed_line.begin(), _smoothed_line.end(), data); } void spec::get_peaks(std::back_insert_iterator > peaks) const { std::copy(_peaks.begin(), _peaks.end(), peaks); } QVariant spec::get_harms_sets() const { return QVariant::fromValue(_harms_sets); } int spec::harms_sets_count() const { return _harms_sets.count(); } spec_harms *spec::harms_set(uint idx) const { if(idx < _harms_sets.count()) return _harms_sets[idx]; return NULL; } void spec::get_harms(std::back_insert_iterator> harms) { for(auto harms_set : _harms_sets) { if(harms_set->is_visible()) harms_set->get_harms(harms); } } void spec::get_humps(std::back_insert_iterator> humps) { std::copy(_humps_sets.begin(), _humps_sets.end(), humps); } qreal spec::harm_tolerance() const { return _harm_tolerance; } qreal spec::get_amplitude(qreal freq) const { return get_y(freq, *this); } void spec::get_state(QJsonObject &root) const { root.insert("name", name()); root.insert("type", type() == gtl::math::spec::ausp ? "ausp" : "spen"); root.insert("unit", gtl::math::spec::unit() == gtl::unit ? "units" : "db"); root.insert("res", gtl::math::spec::resolution()); root.insert("avg", (int)gtl::math::spec::average()); root.insert("overlap", (int)gtl::math::spec::overlap()); root.insert("window", (int)gtl::math::spec::window()); analog_data* analog_data_root = ad()->analog_data_root(); if(analog_data_root) root.insert("ad_name", analog_data_root->name()); else root.insert("ad_name", ""); QJsonObject spec; spec.insert("color", _color); spec.insert("data", data()); root.insert("spec", spec); QJsonObject base_; base_.insert("color", _smoothed_line_color); base_.insert("factor", _smoothing_factor); base_.insert("data", base()); root.insert("base", base_); QJsonObject peaks; peaks.insert("level", _peak_level); QJsonArray peaks_indices; for(auto it = _peaks.begin(); it != _peaks.end(); it++) peaks_indices.append(*it); peaks.insert("indices", peaks_indices); root.insert("peaks", peaks); QJsonObject harmonics; harmonics.insert("tolerance", _harm_tolerance); QJsonArray harms_sets; for(auto harm_set : _harms_sets) { QJsonObject harm_set_object; harm_set->save_state(harm_set_object); harms_sets.append(harm_set_object); } harmonics.insert("harms_sets", harms_sets); root.insert("harmonics", harmonics); } void spec::set_state(const QJsonObject &root) { _name = root["name"].toString(); analog_data* analog_data_root = ad()->analog_data_root(); if(analog_data_root) analog_data_root->set_name(root["ad_name"].toString()); _type = (gtl::math::spec::types)(root["type"].toString() == "spen"); set_unit((gtl::units)(root["unit"].toString() == "db")); set_average(root["avg"].toInt()); set_overlap(root["overlap"].toInt()); set_window((gtl::math::spec::windows)root["window"].toInt()); QJsonObject spec = root["spec"].toObject(); QJsonArray spec_data = spec["data"].toArray(); set_color(spec["color"].toInt()); set_frequency(root["res"].toDouble() * (spec_data.size() - 1)); set_lines(spec_data.size() - 1); for(int i = 0; i < size(); i++) (*this)[i] = spec_data[i].toDouble(); QJsonObject base = root["base"].toObject(); QJsonArray base_data = base["data"].toArray(); set_smoothed_line_color(base["color"].toInt()); _smoothed_line.clear(); for(int i = 0; i < base_data.size(); i++) _smoothed_line.push_back(base_data[i].toDouble()); QJsonObject peaks = root["peaks"].toObject(); _peak_level = peaks["level"].toDouble(); QJsonArray peak_indices = peaks["indices"].toArray(); _peaks.clear(); for(auto it: peak_indices) { _peaks.push_back(it.toInt()); _peaks_freqs.push_back(_peaks.back()*resolution()); } QJsonObject harmonics = root["harmonics"].toObject(); _harm_tolerance = harmonics["torlerance"].toDouble(); QJsonArray harms_set = harmonics["harms_sets"].toArray(); for(auto it: harms_set) { spec_harms *harms = new spec_harms(this); harms->set_tolerance(_harm_tolerance); harms->restore_state(it.toObject()); _harms_sets.push_back(harms); connect(harms, &spec_harms::get_peak_freq, this, &spec::get_peak_freq); connect(harms, &spec_harms::get_harm_amplitude, this, &spec::get_ampl); connect(harms, &spec_harms::get_harm_base, this, &spec::get_base); connect(harms, &spec_harms::visible_changed, this, &spec::harms_set_visible_changed); connect(harms, &spec_harms::get_max_harm_level, this, &spec::get_max_harm_level); } set_max_harm_ampl(); emit spec_changed(); } qreal spec::integral_index() const { qreal integral_index = 0; for(auto harms_set : _harms_sets) integral_index += harms_set->integral_index(); return integral_index; } void spec::get_avg(int points, const_iterator begin, const_iterator end, std::back_insert_iterator > out) { for(auto it = begin; it != end; it++) { int cnt = 0; qreal value = 0; for( auto it1 = (std::distance(begin, it) < points ? begin : it - points); it1 < (std::distance(it, end) <= points ? end : it + 1 + points); it1++ ) { value += *it1; cnt++; } out = value/cnt; out++; } } qreal spec::get_y(qreal x, const std::vector &series) const { if(qIsNaN(x)) return 0; if(series.size() < 2) return 0; qreal x_[2]; qreal y_[2]; int idx = 0; qreal dx = resolution(); if(x < 0) { //idx = 0; return 0; } else if(x > (series.size() - 1)*dx) { //idx = size() - 2; return 0; } else { idx = x/dx; } x_[0] = idx*dx; x_[1] = (idx + 1) * dx; y_[0] = series[idx]; y_[1] = series[idx + 1]; qreal a = (y_[0] - y_[1])/(x_[0] - x_[1]); qreal b = y_[0] - a*x_[0]; return a*x + b; } QJsonArray spec::base() const { QJsonArray base; for(auto it = _smoothed_line.begin(); it != _smoothed_line.end(); it++) base.append(*it); return base; } QJsonArray spec::data() const { QJsonArray data; for(auto it = begin(); it != end(); it++) data.append(*it); return data; } QJsonArray spec::env() const { QJsonArray evl; if(_envelope) { for(auto it : *_envelope) evl.append(it); } return evl; } void spec::before_updating() { _smoothed_line.clear(); _peaks.clear(); _peaks_freqs.clear(); } void spec::after_updating() { get_avg(_smoothing_factor, begin(), end(), std::back_inserter(_smoothed_line)); /* for(int i = 0; i < size(); i++) { int cnt = 0; qreal value = 0; for(int j = qMax(0, i - _smoothing_factor); j <= qMin(i + _smoothing_factor, (int)size() - 1); j++) { value += at(j); cnt++; } _smoothed_line.push_back(value/cnt); } */ int peak_idx = -1; qreal peak_value = 0; qreal df = 1; for(int i = 0; i < size(); i++) { if(at(i) - _smoothed_line[i] >= _peak_level) { if(peak_idx == -1) { peak_idx = i; peak_value = at(i); } else if(at(i) > peak_value) { peak_idx = i; peak_value = at(i); } } else if(at(i) < _smoothed_line[i] || i == (int)size() - 1) { if(peak_idx != -1) { _peaks.push_back(peak_idx); _peaks_freqs.push_back(peak_idx*resolution()); } peak_idx = -1; } } for(auto harms_set : _harms_sets) harms_set->update(); for(auto humps : _humps_sets) humps->update(); set_max_harm_ampl(); emit spec_changed(); } qreal spec::get_freq_for_max_ampl(qreal freq, qreal tolerance) { qreal freq_left = freq - tolerance; qreal freq_right = freq + tolerance; qreal value = freq_left; qreal ampl_max = get_y(freq_left, *this); qreal ampl = get_y(freq_right, *this); if(ampl > ampl_max) { value = freq_right; ampl_max = ampl; } int left = std::max(freq_left/resolution() + 1, 0); int right = std::min(freq_right/resolution(), size() - 1); for(int i = left; i <= right; i++) { ampl = get_y(i*resolution(), *this); if(ampl > ampl_max) { value = i*resolution(); ampl_max = ampl; } } return value; } void spec::set_max_harm_ampl() { _max_harm_level = 0; for(auto harms_set : _harms_sets) harms_set->get_max_ampl(_max_harm_level); emit integral_index_changed(); } void spec::set_name(QString value) { if(value != _name) { _name = value; emit name_changed(); } } void spec::set_color(int value) { if(value != _color) { _color = value; emit color_changed(); } } void spec::set_visible(bool value) { if(_is_visible != value) { _is_visible = value; emit visible_changed(); } } void spec::set_smoothing_factor(int value) { if(_smoothing_factor != value) { _smoothing_factor = value; emit smoothing_factor_changed(); } } void spec::set_smoothed_line_color(int value) { if(_smoothed_line_color != value) { _smoothed_line_color = value; emit smoothed_line_color_changed(); } } void spec::set_peak_level(qreal value) { if(_peak_level != value) { _peak_level = value; emit peak_level_changed(); } } QJSValue spec::add_harms_set(qreal freq, int k, int color, qreal weight) { spec_harms *harms = new spec_harms(this, freq, k, color, weight); harms->set_tolerance(_harm_tolerance); _harms_sets.push_back(harms); connect(harms, &spec_harms::get_peak_freq, this, &spec::get_peak_freq); connect(harms, &spec_harms::get_harm_amplitude, this, &spec::get_ampl); connect(harms, &spec_harms::get_harm_base, this, &spec::get_base); connect(harms, &spec_harms::visible_changed, this, &spec::harms_set_visible_changed); connect(harms, &spec_harms::get_max_harm_level, this, &spec::get_max_harm_level); set_max_harm_ampl(); emit harms_sets_changed(); QJSValue js_object; emit create_engine_object(harms, js_object); return js_object; } QJSValue spec::addHarmsSet(const QJsonObject args) { return add_harms_set( args.value("freq").toDouble(100), args.value("count").toInt(5), args.value("color").toInt(0), args.value("weight").toInt(1) ); } QJSValue spec::addHumps(const QJsonObject& args) { spec_humps *humps = new spec_humps( this, args ); _humps_sets.push_back(humps); QJSValue js_object; emit create_engine_object(humps, js_object); return js_object; } void spec::clear_harms_sets() { for(auto harms : _harms_sets) { delete harms; } _harms_sets.clear(); set_max_harm_ampl(); } void spec::clear_humps() { for(auto humps : _humps_sets) { delete humps; } _humps_sets.clear(); } void spec::set_harm_tolerance(qreal value) { if(value != _harm_tolerance) { _harm_tolerance = value; for(auto it : _harms_sets) it->set_tolerance(_harm_tolerance); emit harm_tolerance_changed(); } } qreal spec::max_harm_level() const { return _max_harm_level; } void spec::get_peak_freq(qreal freq, qreal tolerance, qreal &peak_freq) { if(!_peaks_freqs.empty()) { auto it = std::lower_bound(_peaks_freqs.begin(), _peaks_freqs.end(), freq); if(it != _peaks_freqs.begin()) { if(freq - *prev(it) <= tolerance) { peak_freq = *prev(it); return; } } if(it != _peaks_freqs.end()) { if(*it - freq <= tolerance) { peak_freq = *it; return; } } } peak_freq = -1; } void spec::get_ampl(qreal freq, qreal &res) { res = get_y(freq, *this); } void spec::get_base(qreal freq, qreal &res) { res = get_y(freq, _smoothed_line); } void spec::reset() { _smoothed_line.clear(); _peaks.clear(); _peaks_freqs.clear(); } void spec::get_max_harm_level(qreal &value) const { value = _max_harm_level; } } }