]> sigrok.org Git - pulseview.git/blobdiff - pv/views/trace/analogsignal.cpp
Implement MathSignal
[pulseview.git] / pv / views / trace / analogsignal.cpp
index 60ba2ca9831b0cd94fc95ea85d93156d771ffe06..f64b94ae4d20b4f0791689fa03cfc34544fd93d9 100644 (file)
@@ -65,6 +65,7 @@ using std::vector;
 using pv::data::LogicSegment;
 using pv::data::SignalBase;
 using pv::util::SIPrefix;
+using pv::util::determine_value_prefix;
 
 namespace pv {
 namespace views {
@@ -95,8 +96,8 @@ const int64_t AnalogSignal::TracePaintBlockSize = 1024 * 1024;  // 4 MiB (due to
 const float AnalogSignal::EnvelopeThreshold = 64.0f;
 
 const int AnalogSignal::MaximumVDivs = 10;
-const int AnalogSignal::MinScaleIndex = -6;
-const int AnalogSignal::MaxScaleIndex = 7;
+const int AnalogSignal::MinScaleIndex = -6;  // 0.01 units/div
+const int AnalogSignal::MaxScaleIndex = 10;  // 1000 units/div
 
 const int AnalogSignal::InfoTextMarginRight = 20;
 const int AnalogSignal::InfoTextMarginBottom = 5;
@@ -105,13 +106,13 @@ AnalogSignal::AnalogSignal(
        pv::Session &session,
        shared_ptr<data::SignalBase> base) :
        Signal(session, base),
+       value_at_hover_pos_(std::numeric_limits<float>::quiet_NaN()),
        scale_index_(4), // 20 per div
        pos_vdivs_(1),
        neg_vdivs_(1),
        resolution_(0),
        display_type_(DisplayBoth),
-       autoranging_(true),
-       value_at_hover_pos_(std::numeric_limits<float>::quiet_NaN())
+       autoranging_(true)
 {
        axis_pen_ = AxisPen;
 
@@ -143,38 +144,48 @@ shared_ptr<pv::data::SignalData> AnalogSignal::data() const
        return base_->analog_data();
 }
 
-void AnalogSignal::save_settings(QSettings &settings) const
+std::map<QString, QVariant> AnalogSignal::save_settings() const
 {
-       settings.setValue("pos_vdivs", pos_vdivs_);
-       settings.setValue("neg_vdivs", neg_vdivs_);
-       settings.setValue("scale_index", scale_index_);
-       settings.setValue("display_type", display_type_);
-       settings.setValue("autoranging", autoranging_);
-       settings.setValue("div_height", div_height_);
+       std::map<QString, QVariant> result;
+
+       result["pos_vdivs"] = pos_vdivs_;
+       result["neg_vdivs"] = neg_vdivs_;
+       result["scale_index"] = scale_index_;
+       result["display_type"] = display_type_;
+       result["autoranging"] = pos_vdivs_;
+       result["div_height"] = div_height_;
+
+       return result;
 }
 
-void AnalogSignal::restore_settings(QSettings &settings)
+void AnalogSignal::restore_settings(std::map<QString, QVariant> settings)
 {
-       if (settings.contains("pos_vdivs"))
-               pos_vdivs_ = settings.value("pos_vdivs").toInt();
+       auto entry = settings.find("pos_vdivs");
+       if (entry != settings.end())
+               pos_vdivs_ = settings["pos_vdivs"].toInt();
 
-       if (settings.contains("neg_vdivs"))
-               neg_vdivs_ = settings.value("neg_vdivs").toInt();
+       entry = settings.find("neg_vdivs");
+       if (entry != settings.end())
+               neg_vdivs_ = settings["neg_vdivs"].toInt();
 
-       if (settings.contains("scale_index")) {
-               scale_index_ = settings.value("scale_index").toInt();
+       entry = settings.find("scale_index");
+       if (entry != settings.end()) {
+               scale_index_ = settings["scale_index"].toInt();
                update_scale();
        }
 
-       if (settings.contains("display_type"))
-               display_type_ = (DisplayType)(settings.value("display_type").toInt());
+       entry = settings.find("display_type");
+       if (entry != settings.end())
+               display_type_ = (DisplayType)(settings["display_type"].toInt());
 
-       if (settings.contains("autoranging"))
-               autoranging_ = settings.value("autoranging").toBool();
+       entry = settings.find("autoranging");
+       if (entry != settings.end())
+               autoranging_ = settings["autoranging"].toBool();
 
-       if (settings.contains("div_height")) {
+       entry = settings.find("div_height");
+       if (entry != settings.end()) {
                const int old_height = div_height_;
-               div_height_ = settings.value("div_height").toInt();
+               div_height_ = settings["div_height"].toInt();
 
                if ((div_height_ != old_height) && owner_) {
                        // Call order is important, otherwise the lazy event handler won't work
@@ -292,12 +303,18 @@ void AnalogSignal::paint_fore(QPainter &p, ViewItemPaintParams &pp)
 
                QString infotext;
 
+               SIPrefix prefix;
+               if (fabs(signal_max_) > fabs(signal_min_))
+                       prefix = determine_value_prefix(fabs(signal_max_));
+               else
+                       prefix = determine_value_prefix(fabs(signal_min_));
+
                // Show the info section on the right side of the trace, including
                // the value at the hover point when the hover marker is enabled
                // and we have corresponding data available
                if (show_hover_marker_ && !std::isnan(value_at_hover_pos_)) {
                        infotext = QString("[%1] %2 V/div")
-                               .arg(format_value_si(value_at_hover_pos_, SIPrefix::unspecified, 2, "V", false))
+                               .arg(format_value_si(value_at_hover_pos_, prefix, 3, "V", false))
                                .arg(resolution_);
                } else
                        infotext = QString("%1 V/div").arg(resolution_);
@@ -444,7 +461,9 @@ void AnalogSignal::paint_trace(QPainter &p,
        }
        delete[] sample_block;
 
-       p.drawPolyline(points, points_count);
+       // QPainter::drawPolyline() is slow, let's paint the lines ourselves
+       for (int64_t i = 1; i < points_count; i++)
+               p.drawLine(points[i - 1], points[i]);
 
        if (show_sampling_points) {
                if (paint_thr_dots) {
@@ -840,7 +859,6 @@ void AnalogSignal::perform_autoranging(bool keep_divs, bool force_update)
        if (segments.empty())
                return;
 
-       static double prev_min = 0, prev_max = 0;
        double min = 0, max = 0;
 
        for (const shared_ptr<pv::data::AnalogSegment>& segment : segments) {
@@ -849,11 +867,11 @@ void AnalogSignal::perform_autoranging(bool keep_divs, bool force_update)
                max = std::max(max, mm.second);
        }
 
-       if ((min == prev_min) && (max == prev_max) && !force_update)
+       if ((min == signal_min_) && (max == signal_max_) && !force_update)
                return;
 
-       prev_min = min;
-       prev_max = max;
+       signal_min_ = min;
+       signal_max_ = max;
 
        // If we're allowed to alter the div assignment...
        if (!keep_divs) {
@@ -965,22 +983,20 @@ void AnalogSignal::populate_popup_form(QWidget *parent, QFormLayout *form)
        // Add the standard options
        Signal::populate_popup_form(parent, form);
 
-       QFormLayout *const layout = new QFormLayout;
-
        // Add div-related settings
        pvdiv_sb_ = new QSpinBox(parent);
        pvdiv_sb_->setRange(0, MaximumVDivs);
        pvdiv_sb_->setValue(pos_vdivs_);
        connect(pvdiv_sb_, SIGNAL(valueChanged(int)),
                this, SLOT(on_pos_vdivs_changed(int)));
-       layout->addRow(tr("Number of pos vertical divs"), pvdiv_sb_);
+       form->addRow(tr("Number of pos vertical divs"), pvdiv_sb_);
 
        nvdiv_sb_ = new QSpinBox(parent);
        nvdiv_sb_->setRange(0, MaximumVDivs);
        nvdiv_sb_->setValue(neg_vdivs_);
        connect(nvdiv_sb_, SIGNAL(valueChanged(int)),
                this, SLOT(on_neg_vdivs_changed(int)));
-       layout->addRow(tr("Number of neg vertical divs"), nvdiv_sb_);
+       form->addRow(tr("Number of neg vertical divs"), nvdiv_sb_);
 
        div_height_sb_ = new QSpinBox(parent);
        div_height_sb_->setRange(20, 1000);
@@ -989,7 +1005,7 @@ void AnalogSignal::populate_popup_form(QWidget *parent, QFormLayout *form)
        div_height_sb_->setValue(div_height_);
        connect(div_height_sb_, SIGNAL(valueChanged(int)),
                this, SLOT(on_div_height_changed(int)));
-       layout->addRow(tr("Div height"), div_height_sb_);
+       form->addRow(tr("Div height"), div_height_sb_);
 
        // Add the vertical resolution
        resolution_cb_ = new QComboBox(parent);
@@ -1010,7 +1026,7 @@ void AnalogSignal::populate_popup_form(QWidget *parent, QFormLayout *form)
        vdiv_layout->addWidget(resolution_cb_, 0, 0);
        vdiv_layout->addWidget(vdiv_unit, 0, 1);
 
-       layout->addRow(tr("Vertical resolution"), vdiv_layout);
+       form->addRow(tr("Vertical resolution"), vdiv_layout);
 
        // Add the autoranging checkbox
        QCheckBox* autoranging_cb = new QCheckBox();
@@ -1019,7 +1035,7 @@ void AnalogSignal::populate_popup_form(QWidget *parent, QFormLayout *form)
        connect(autoranging_cb, SIGNAL(stateChanged(int)),
                this, SLOT(on_autoranging_changed(int)));
 
-       layout->addRow(tr("Autoranging"), autoranging_cb);
+       form->addRow(tr("Autoranging"), autoranging_cb);
 
        // Add the conversion type dropdown
        conversion_cb_ = new QComboBox();
@@ -1034,7 +1050,7 @@ void AnalogSignal::populate_popup_form(QWidget *parent, QFormLayout *form)
        cur_idx = conversion_cb_->findData(QVariant(base_->get_conversion_type()));
        conversion_cb_->setCurrentIndex(cur_idx);
 
-       layout->addRow(tr("Conversion"), conversion_cb_);
+       form->addRow(tr("Conversion"), conversion_cb_);
 
        connect(conversion_cb_, SIGNAL(currentIndexChanged(int)),
                this, SLOT(on_conversion_changed(int)));
@@ -1043,7 +1059,7 @@ void AnalogSignal::populate_popup_form(QWidget *parent, QFormLayout *form)
     conv_threshold_cb_ = new QComboBox();
     conv_threshold_cb_->setEditable(true);
 
-    layout->addRow(tr("Conversion threshold(s)"), conv_threshold_cb_);
+    form->addRow(tr("Conversion threshold(s)"), conv_threshold_cb_);
 
     connect(conv_threshold_cb_, SIGNAL(currentIndexChanged(int)),
             this, SLOT(on_conv_threshold_changed(int)));
@@ -1060,15 +1076,13 @@ void AnalogSignal::populate_popup_form(QWidget *parent, QFormLayout *form)
        cur_idx = display_type_cb_->findData(QVariant(display_type_));
        display_type_cb_->setCurrentIndex(cur_idx);
 
-       layout->addRow(tr("Show traces for"), display_type_cb_);
+       form->addRow(tr("Show traces for"), display_type_cb_);
 
        connect(display_type_cb_, SIGNAL(currentIndexChanged(int)),
                this, SLOT(on_display_type_changed(int)));
 
        // Update the conversion widget contents and states
        update_conversion_widgets();
-
-       form->addRow(layout);
 }
 
 void AnalogSignal::hover_point_changed(const QPoint &hp)
@@ -1081,11 +1095,10 @@ void AnalogSignal::hover_point_changed(const QPoint &hp)
        if (hp.x() <= 0) {
                value_at_hover_pos_ = std::numeric_limits<float>::quiet_NaN();
        } else {
-               try {
+               if ((size_t)hp.x() < value_at_pixel_pos_.size())
                        value_at_hover_pos_ = value_at_pixel_pos_.at(hp.x());
-               } catch (out_of_range&) {
+               else
                        value_at_hover_pos_ = std::numeric_limits<float>::quiet_NaN();
-               }
        }
 }
 
@@ -1115,11 +1128,12 @@ void AnalogSignal::on_setting_changed(const QString &key, const QVariant &value)
 
 void AnalogSignal::on_min_max_changed(float min, float max)
 {
-       (void)min;
-       (void)max;
-
        if (autoranging_)
                perform_autoranging(false, false);
+       else {
+               if (min < signal_min_) signal_min_ = min;
+               if (max > signal_max_) signal_max_ = max;
+       }
 }
 
 void AnalogSignal::on_pos_vdivs_changed(int vdivs)