632 lines
18 KiB
C++
632 lines
18 KiB
C++
#include "gtl_scr_spec.h"
|
|
#include "qvariant.h"
|
|
|
|
#include <QDebug>
|
|
|
|
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<std::vector<qreal> > data) const
|
|
{
|
|
std::copy(_smoothed_line.begin(), _smoothed_line.end(), data);
|
|
}
|
|
|
|
void spec::get_peaks(std::back_insert_iterator<std::vector<int> > 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<std::vector<spec_harm *>> 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<std::vector<spec_humps *>> 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<std::vector<qreal> > 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<qreal> &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<int>(freq_left/resolution() + 1, 0);
|
|
int right = std::min<int>(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;
|
|
}
|
|
}
|
|
}
|