X-Git-Url: https://sigrok.org/gitweb/?p=pulseview.git;a=blobdiff_plain;f=pv%2Fview%2Fanalogsignal.cpp;h=106735567c78e11363a2c0d9cda6d750c0b1f937;hp=07fb0ef89fa8ab59c2930c5be11972388377f0cd;hb=17c7f31c7bb674d5017a8de66a5816e34542abb8;hpb=73e377feaaf01e790c87a4a0c918a36e87a9a6b9 diff --git a/pv/view/analogsignal.cpp b/pv/view/analogsignal.cpp index 07fb0ef8..10673556 100644 --- a/pv/view/analogsignal.cpp +++ b/pv/view/analogsignal.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -30,22 +31,30 @@ #include #include #include -#include #include #include "analogsignal.hpp" #include "pv/data/analog.hpp" #include "pv/data/analogsegment.hpp" +#include "pv/data/logic.hpp" +#include "pv/data/logicsegment.hpp" #include "pv/data/signalbase.hpp" +#include "pv/globalsettings.hpp" +#include "pv/view/logicsignal.hpp" #include "pv/view/view.hpp" #include +using std::deque; +using std::div; +using std::div_t; using std::max; using std::make_pair; using std::min; +using std::numeric_limits; +using std::pair; using std::shared_ptr; -using std::deque; +using std::vector; namespace pv { namespace views { @@ -58,10 +67,13 @@ const QColor AnalogSignal::SignalColours[4] = { QColor(0x4E, 0x9A, 0x06) // Green }; -const QColor AnalogSignal::GridMajorColor = QColor(0, 0, 0, 40*256/100); -const QColor AnalogSignal::GridMinorColor = QColor(0, 0, 0, 20*256/100); +const QColor AnalogSignal::GridMajorColor = QColor(0, 0, 0, 40 * 256 / 100); +const QColor AnalogSignal::GridMinorColor = QColor(0, 0, 0, 20 * 256 / 100); + +const QColor AnalogSignal::SamplingPointColour(0x77, 0x77, 0x77); -const float AnalogSignal::EnvelopeThreshold = 256.0f; +const int64_t AnalogSignal::TracePaintBlockSize = 1024 * 1024; // 4 MiB (due to float) +const float AnalogSignal::EnvelopeThreshold = 64.0f; const int AnalogSignal::MaximumVDivs = 10; const int AnalogSignal::MinScaleIndex = -6; @@ -80,8 +92,16 @@ AnalogSignal::AnalogSignal( pos_vdivs_(1), neg_vdivs_(1), resolution_(0), - autoranging_(1) + conversion_type_(data::SignalBase::NoConversion), + display_type_(DisplayBoth), + autoranging_(true) { + pv::data::Analog* analog_data = + dynamic_cast(data().get()); + + connect(analog_data, SIGNAL(samples_added(QObject*, uint64_t, uint64_t)), + this, SLOT(on_samples_added())); + base_->set_colour(SignalColours[base_->index() % countof(SignalColours)]); update_scale(); } @@ -96,6 +116,8 @@ void AnalogSignal::save_settings(QSettings &settings) const settings.setValue("pos_vdivs", pos_vdivs_); settings.setValue("neg_vdivs", neg_vdivs_); settings.setValue("scale_index", scale_index_); + settings.setValue("conversion_type", conversion_type_); + settings.setValue("display_type", display_type_); settings.setValue("autoranging", autoranging_); } @@ -112,11 +134,19 @@ void AnalogSignal::restore_settings(QSettings &settings) update_scale(); } + if (settings.contains("conversion_type")) { + conversion_type_ = (data::SignalBase::ConversionType)(settings.value("conversion_type").toInt()); + update_conversion_type(); + } + + if (settings.contains("display_type")) + display_type_ = (DisplayType)(settings.value("display_type").toInt()); + if (settings.contains("autoranging")) autoranging_ = settings.value("autoranging").toBool(); } -std::pair AnalogSignal::v_extents() const +pair AnalogSignal::v_extents() const { const int ph = pos_vdivs_ * div_height_; const int nh = neg_vdivs_ * div_height_; @@ -127,16 +157,14 @@ int AnalogSignal::scale_handle_offset() const { const int h = (pos_vdivs_ + neg_vdivs_) * div_height_; - return ((scale_index_drag_offset_ - scale_index_) * - h / 4) - h / 2; + return ((scale_index_drag_offset_ - scale_index_) * h / 4) - h / 2; } void AnalogSignal::scale_handle_dragged(int offset) { const int h = (pos_vdivs_ + neg_vdivs_) * div_height_; - scale_index_ = scale_index_drag_offset_ - - (offset + h / 2) / (h / 4); + scale_index_ = scale_index_drag_offset_ - (offset + h / 2) / (h / 4); update_scale(); } @@ -147,7 +175,7 @@ void AnalogSignal::scale_handle_drag_release() update_scale(); } -void AnalogSignal::paint_back(QPainter &p, const ViewItemPaintParams &pp) +void AnalogSignal::paint_back(QPainter &p, ViewItemPaintParams &pp) { if (base_->enabled()) { Trace::paint_back(p, pp); @@ -155,7 +183,7 @@ void AnalogSignal::paint_back(QPainter &p, const ViewItemPaintParams &pp) } } -void AnalogSignal::paint_mid(QPainter &p, const ViewItemPaintParams &pp) +void AnalogSignal::paint_mid(QPainter &p, ViewItemPaintParams &pp) { assert(base_->analog_data()); assert(owner_); @@ -165,71 +193,89 @@ void AnalogSignal::paint_mid(QPainter &p, const ViewItemPaintParams &pp) if (!base_->enabled()) return; - paint_grid(p, y, pp.left(), pp.right()); - - const deque< shared_ptr > &segments = - base_->analog_data()->analog_segments(); - if (segments.empty()) - return; - - const shared_ptr &segment = - segments.front(); - - const double pixels_offset = pp.pixels_offset(); - const double samplerate = max(1.0, segment->samplerate()); - const pv::util::Timestamp& start_time = segment->start_time(); - const int64_t last_sample = segment->get_sample_count() - 1; - const double samples_per_pixel = samplerate * pp.scale(); - const pv::util::Timestamp start = samplerate * (pp.offset() - start_time); - const pv::util::Timestamp end = start + samples_per_pixel * pp.width(); + if ((display_type_ == DisplayAnalog) || (display_type_ == DisplayBoth)) { + paint_grid(p, y, pp.left(), pp.right()); + + const deque< shared_ptr > &segments = + base_->analog_data()->analog_segments(); + if (segments.empty()) + return; + + const shared_ptr &segment = + segments.front(); + + const double pixels_offset = pp.pixels_offset(); + const double samplerate = max(1.0, segment->samplerate()); + const pv::util::Timestamp& start_time = segment->start_time(); + const int64_t last_sample = segment->get_sample_count() - 1; + const double samples_per_pixel = samplerate * pp.scale(); + const pv::util::Timestamp start = samplerate * (pp.offset() - start_time); + const pv::util::Timestamp end = start + samples_per_pixel * pp.width(); + + const int64_t start_sample = min(max(floor(start).convert_to(), + (int64_t)0), last_sample); + const int64_t end_sample = min(max((ceil(end) + 1).convert_to(), + (int64_t)0), last_sample); + + if (samples_per_pixel < EnvelopeThreshold) + paint_trace(p, segment, y, pp.left(), + start_sample, end_sample, + pixels_offset, samples_per_pixel); + else + paint_envelope(p, segment, y, pp.left(), + start_sample, end_sample, + pixels_offset, samples_per_pixel); + } - const int64_t start_sample = min(max(floor(start).convert_to(), - (int64_t)0), last_sample); - const int64_t end_sample = min(max((ceil(end) + 1).convert_to(), - (int64_t)0), last_sample); + if ((display_type_ == DisplayConverted) || (display_type_ == DisplayBoth)) { + if (((conversion_type_ == data::SignalBase::A2LConversionByTreshold) || + (conversion_type_ == data::SignalBase::A2LConversionBySchmittTrigger))) { - if (samples_per_pixel < EnvelopeThreshold) - paint_trace(p, segment, y, pp.left(), - start_sample, end_sample, - pixels_offset, samples_per_pixel); - else - paint_envelope(p, segment, y, pp.left(), - start_sample, end_sample, - pixels_offset, samples_per_pixel); + paint_logic_mid(p, pp); + } + } } -void AnalogSignal::paint_fore(QPainter &p, const ViewItemPaintParams &pp) +void AnalogSignal::paint_fore(QPainter &p, ViewItemPaintParams &pp) { if (!enabled()) return; - const int y = get_visual_y(); + if ((display_type_ == DisplayAnalog) || (display_type_ == DisplayBoth)) { + const int y = get_visual_y(); - // Show the info section on the right side of the trace - const QString infotext = QString("%1 V/div").arg(resolution_); + // Show the info section on the right side of the trace + const QString infotext = QString("%1 V/div").arg(resolution_); - p.setPen(base_->colour()); - p.setFont(QApplication::font()); + p.setPen(base_->colour()); + p.setFont(QApplication::font()); - const QRectF bounding_rect = QRectF(pp.left(), - y + v_extents().first, - pp.width() - InfoTextMarginRight, - v_extents().second - v_extents().first - InfoTextMarginBottom); + const QRectF bounding_rect = QRectF(pp.left(), + y + v_extents().first, + pp.width() - InfoTextMarginRight, + v_extents().second - v_extents().first - InfoTextMarginBottom); - p.drawText(bounding_rect, Qt::AlignRight | Qt::AlignBottom, infotext); + p.drawText(bounding_rect, Qt::AlignRight | Qt::AlignBottom, infotext); + } } void AnalogSignal::paint_grid(QPainter &p, int y, int left, int right) { p.setRenderHint(QPainter::Antialiasing, false); + GlobalSettings settings; + const bool show_analog_minor_grid = + settings.value(GlobalSettings::Key_View_ShowAnalogMinorGrid).toBool(); + if (pos_vdivs_ > 0) { p.setPen(QPen(GridMajorColor, 1, Qt::DashLine)); for (int i = 1; i <= pos_vdivs_; i++) { const float dy = i * div_height_; p.drawLine(QLineF(left, y - dy, right, y - dy)); } + } + if ((pos_vdivs_ > 0) && show_analog_minor_grid) { p.setPen(QPen(GridMinorColor, 1, Qt::DashLine)); for (int i = 0; i < pos_vdivs_; i++) { const float dy = i * div_height_; @@ -248,7 +294,9 @@ void AnalogSignal::paint_grid(QPainter &p, int y, int left, int right) const float dy = i * div_height_; p.drawLine(QLineF(left, y + dy, right, y + dy)); } + } + if ((pos_vdivs_ > 0) && show_analog_minor_grid) { p.setPen(QPen(GridMinorColor, 1, Qt::DashLine)); for (int i = 0; i < neg_vdivs_; i++) { const float dy = i * div_height_; @@ -269,24 +317,59 @@ void AnalogSignal::paint_trace(QPainter &p, int y, int left, const int64_t start, const int64_t end, const double pixels_offset, const double samples_per_pixel) { + if (end <= start) + return; + + // Calculate and paint the sampling points if enabled and useful + GlobalSettings settings; + const bool show_sampling_points = + settings.value(GlobalSettings::Key_View_ShowSamplingPoints).toBool() && + (samples_per_pixel < 0.25); + p.setPen(base_->colour()); - QPointF *points = new QPointF[end - start]; + const int64_t points_count = end - start; + + QPointF *points = new QPointF[points_count]; QPointF *point = points; - pv::data::SegmentAnalogDataIterator* it = - segment->begin_sample_iteration(start); + QRectF *sampling_points = nullptr; + if (show_sampling_points) + sampling_points = new QRectF[points_count]; + QRectF *sampling_point = sampling_points; + + int64_t sample_count = min(points_count, TracePaintBlockSize); + int64_t block_sample = 0; + const float *sample_block = segment->get_samples(start, start + sample_count); + + const int w = 2; + for (int64_t sample = start; sample != end; sample++, block_sample++) { + + if (block_sample == TracePaintBlockSize) { + block_sample = 0; + delete[] sample_block; + sample_count = min(points_count - sample, TracePaintBlockSize); + sample_block = segment->get_samples(sample, sample + sample_count); + } - for (int64_t sample = start; sample != end; sample++) { const float x = (sample / samples_per_pixel - pixels_offset) + left; - *point++ = QPointF(x, y - *((float*)it->value) * scale_); - segment->continue_sample_iteration(it, 1); + *point++ = QPointF(x, y - sample_block[block_sample] * scale_); + + if (show_sampling_points) + *sampling_point++ = + QRectF(x - (w / 2), y - sample_block[block_sample] * scale_ - (w / 2), w, w); } - segment->end_sample_iteration(it); + delete[] sample_block; + + p.drawPolyline(points, points_count); - p.drawPolyline(points, point - points); + if (show_sampling_points) { + p.setPen(SamplingPointColour); + p.drawRects(sampling_points, points_count); + delete[] sampling_points; + } delete[] points; } @@ -310,7 +393,7 @@ void AnalogSignal::paint_envelope(QPainter &p, QRectF *const rects = new QRectF[e.length]; QRectF *rect = rects; - for (uint64_t sample = 0; sample < e.length-1; sample++) { + for (uint64_t sample = 0; sample < e.length - 1; sample++) { const float x = ((e.scale * sample + e.start) / samples_per_pixel - pixels_offset) + left; const AnalogSegment::EnvelopeSample *const s = @@ -318,8 +401,8 @@ void AnalogSignal::paint_envelope(QPainter &p, // We overlap this sample with the next so that vertical // gaps do not appear during steep rising or falling edges - const float b = y - max(s->max, (s+1)->min) * scale_; - const float t = y - min(s->min, (s+1)->max) * scale_; + const float b = y - max(s->max, (s + 1)->min) * scale_; + const float t = y - min(s->min, (s + 1)->max) * scale_; float h = b - t; if (h >= 0.0f && h <= 1.0f) @@ -336,13 +419,136 @@ void AnalogSignal::paint_envelope(QPainter &p, delete[] e.samples; } +void AnalogSignal::paint_logic_mid(QPainter &p, ViewItemPaintParams &pp) +{ + QLineF *line; + + vector< pair > edges; + + assert(base_); + + const int y = get_visual_y(); + + if (!base_->enabled() || !base_->logic_data()) + return; + + const int signal_margin = + QFontMetrics(QApplication::font()).height() / 2; + + const int ph = min(pos_vdivs_, 1) * div_height_; + const int nh = min(neg_vdivs_, 1) * div_height_; + const float high_offset = y - ph + signal_margin + 0.5f; + const float low_offset = y + nh - signal_margin - 0.5f; + + const deque< shared_ptr > &segments = + base_->logic_data()->logic_segments(); + + if (segments.empty()) + return; + + const shared_ptr &segment = + segments.front(); + + double samplerate = segment->samplerate(); + + // Show sample rate as 1Hz when it is unknown + if (samplerate == 0.0) + samplerate = 1.0; + + const double pixels_offset = pp.pixels_offset(); + const pv::util::Timestamp& start_time = segment->start_time(); + const int64_t last_sample = segment->get_sample_count() - 1; + const double samples_per_pixel = samplerate * pp.scale(); + const pv::util::Timestamp start = samplerate * (pp.offset() - start_time); + const pv::util::Timestamp end = start + samples_per_pixel * pp.width(); + + const int64_t start_sample = min(max(floor(start).convert_to(), + (int64_t)0), last_sample); + const uint64_t end_sample = min(max(ceil(end).convert_to(), + (int64_t)0), last_sample); + + segment->get_subsampled_edges(edges, start_sample, end_sample, + samples_per_pixel / LogicSignal::Oversampling, 0); + assert(edges.size() >= 2); + + // Paint the edges + const unsigned int edge_count = edges.size() - 2; + QLineF *const edge_lines = new QLineF[edge_count]; + line = edge_lines; + + for (auto i = edges.cbegin() + 1; i != edges.cend() - 1; i++) { + const float x = ((*i).first / samples_per_pixel - + pixels_offset) + pp.left(); + *line++ = QLineF(x, high_offset, x, low_offset); + } + + p.setPen(LogicSignal::EdgeColour); + p.drawLines(edge_lines, edge_count); + delete[] edge_lines; + + // Paint the caps + const unsigned int max_cap_line_count = edges.size(); + QLineF *const cap_lines = new QLineF[max_cap_line_count]; + + p.setPen(LogicSignal::HighColour); + paint_logic_caps(p, cap_lines, edges, true, samples_per_pixel, + pixels_offset, pp.left(), high_offset); + p.setPen(LogicSignal::LowColour); + paint_logic_caps(p, cap_lines, edges, false, samples_per_pixel, + pixels_offset, pp.left(), low_offset); + + delete[] cap_lines; + + // Return if we don't need to paint the sampling points + GlobalSettings settings; + const bool show_sampling_points = + settings.value(GlobalSettings::Key_View_ShowSamplingPoints).toBool(); + + if (!show_sampling_points || (samples_per_pixel >= 0.25)) + return; + + // Paint the sampling points + const uint64_t sampling_points_count = end_sample - start_sample + 1; + QRectF *const sampling_points = new QRectF[sampling_points_count]; + QRectF *sampling_point = sampling_points; + + const int w = 1; + const float y_middle = high_offset - ((high_offset - low_offset) / 2); + for (uint64_t i = start_sample; i < end_sample + 1; ++i) { + const float x = (i / samples_per_pixel - pixels_offset) + pp.left(); + *sampling_point++ = QRectF(x - (w / 2), y_middle - (w / 2), w, w); + } + + p.setPen(SamplingPointColour); + p.drawRects(sampling_points, sampling_points_count); + delete[] sampling_points; +} + +void AnalogSignal::paint_logic_caps(QPainter &p, QLineF *const lines, + vector< pair > &edges, bool level, + double samples_per_pixel, double pixels_offset, float x_offset, + float y_offset) +{ + QLineF *line = lines; + + for (auto i = edges.begin(); i != (edges.end() - 1); i++) + if ((*i).second == level) { + *line++ = QLineF( + ((*i).first / samples_per_pixel - + pixels_offset) + x_offset, y_offset, + ((*(i+1)).first / samples_per_pixel - + pixels_offset) + x_offset, y_offset); + } + + p.drawLines(lines, line - lines); +} + float AnalogSignal::get_resolution(int scale_index) { const float seq[] = {1.0f, 2.0f, 5.0f}; - const int offset = std::numeric_limits::max() / (2 * countof(seq)); - const std::div_t d = std::div( - (int)(scale_index + countof(seq) * offset), + const int offset = numeric_limits::max() / (2 * countof(seq)); + const div_t d = div((int)(scale_index + countof(seq) * offset), countof(seq)); return powf(10.0f, d.quot - offset) * seq[d.rem]; @@ -354,7 +560,15 @@ void AnalogSignal::update_scale() scale_ = div_height_ / resolution_; } -void AnalogSignal::perform_autoranging(bool force_update) +void AnalogSignal::update_conversion_type() +{ + base_->set_conversion_type(conversion_type_); + + if (owner_) + owner_->row_item_appearance_changed(false, true); +} + +void AnalogSignal::perform_autoranging(bool keep_divs, bool force_update) { const deque< shared_ptr > &segments = base_->analog_data()->analog_segments(); @@ -366,7 +580,7 @@ void AnalogSignal::perform_autoranging(bool force_update) double min = 0, max = 0; for (shared_ptr segment : segments) { - std::pair mm = segment->get_min_max(); + pair mm = segment->get_min_max(); min = std::min(min, mm.first); max = std::max(max, mm.second); } @@ -377,10 +591,33 @@ void AnalogSignal::perform_autoranging(bool force_update) prev_min = min; prev_max = max; - // Use all divs for the positive range if there are no negative values - if ((min == 0) && (neg_vdivs_ > 0)) { - pos_vdivs_ += neg_vdivs_; - neg_vdivs_ = 0; + // If we're allowed to alter the div assignment... + if (!keep_divs) { + // Use all divs for the positive range if there are no negative values + if ((min == 0) && (neg_vdivs_ > 0)) { + pos_vdivs_ += neg_vdivs_; + neg_vdivs_ = 0; + } + + // Split up the divs if there are negative values but no negative divs + if ((min < 0) && (neg_vdivs_ == 0)) { + neg_vdivs_ = pos_vdivs_ / 2; + pos_vdivs_ -= neg_vdivs_; + } + } + + // If there is still no positive div when we need it, add one + // (this can happen when pos_vdivs==neg_vdivs==0) + if ((max > 0) && (pos_vdivs_ == 0)) { + pos_vdivs_ = 1; + owner_->extents_changed(false, true); + } + + // If there is still no negative div when we need it, add one + // (this can happen when pos_vdivs was 0 or 1 when trying to split) + if ((min < 0) && (neg_vdivs_ == 0)) { + neg_vdivs_ = 1; + owner_->extents_changed(false, true); } double min_value_per_div; @@ -409,19 +646,19 @@ void AnalogSignal::populate_popup_form(QWidget *parent, QFormLayout *form) QFormLayout *const layout = new QFormLayout; // Add the number of vdivs - QSpinBox *pvdiv_sb = new QSpinBox(parent); - pvdiv_sb->setRange(0, MaximumVDivs); - pvdiv_sb->setValue(pos_vdivs_); - connect(pvdiv_sb, SIGNAL(valueChanged(int)), + 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); + layout->addRow(tr("Number of pos vertical divs"), pvdiv_sb_); - QSpinBox *nvdiv_sb = new QSpinBox(parent); - nvdiv_sb->setRange(0, MaximumVDivs); - nvdiv_sb->setValue(neg_vdivs_); - connect(nvdiv_sb, SIGNAL(valueChanged(int)), + 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); + layout->addRow(tr("Number of neg vertical divs"), nvdiv_sb_); // Add the vertical resolution resolution_cb_ = new QComboBox(parent); @@ -431,7 +668,7 @@ void AnalogSignal::populate_popup_form(QWidget *parent, QFormLayout *form) resolution_cb_->insertItem(0, label, QVariant(i)); } - const int cur_idx = resolution_cb_->findData(QVariant(scale_index_)); + int cur_idx = resolution_cb_->findData(QVariant(scale_index_)); resolution_cb_->setCurrentIndex(cur_idx); connect(resolution_cb_, SIGNAL(currentIndexChanged(int)), @@ -453,15 +690,70 @@ void AnalogSignal::populate_popup_form(QWidget *parent, QFormLayout *form) layout->addRow(tr("Autoranging"), autoranging_cb); + // Add the conversion type dropdown + conversion_cb_ = new QComboBox(); + + conversion_cb_->addItem("none", data::SignalBase::NoConversion); + conversion_cb_->addItem("to logic via threshold", data::SignalBase::A2LConversionByTreshold); + conversion_cb_->addItem("to logic via schmitt-trigger", data::SignalBase::A2LConversionBySchmittTrigger); + + cur_idx = conversion_cb_->findData(QVariant(conversion_type_)); + conversion_cb_->setCurrentIndex(cur_idx); + + layout->addRow(tr("Conversion"), conversion_cb_); + + connect(conversion_cb_, SIGNAL(currentIndexChanged(int)), + this, SLOT(on_conversion_changed(int))); + + // Add the display type dropdown + display_type_cb_ = new QComboBox(); + + display_type_cb_->addItem(tr("Analog"), DisplayAnalog); + display_type_cb_->addItem(tr("Converted"), DisplayConverted); + display_type_cb_->addItem(tr("Both"), DisplayBoth); + + cur_idx = display_type_cb_->findData(QVariant(display_type_)); + display_type_cb_->setCurrentIndex(cur_idx); + + layout->addRow(tr("Traces to show:"), display_type_cb_); + + connect(display_type_cb_, SIGNAL(currentIndexChanged(int)), + this, SLOT(on_display_type_changed(int))); + form->addRow(layout); } +void AnalogSignal::on_samples_added() +{ + perform_autoranging(false, false); + + if (owner_) + owner_->row_item_appearance_changed(false, true); +} + void AnalogSignal::on_pos_vdivs_changed(int vdivs) { + if (vdivs == pos_vdivs_) + return; + pos_vdivs_ = vdivs; - if (autoranging_) - perform_autoranging(true); + // There has to be at least one div, positive or negative + if ((neg_vdivs_ == 0) && (pos_vdivs_ == 0)) { + pos_vdivs_ = 1; + if (pvdiv_sb_) + pvdiv_sb_->setValue(pos_vdivs_); + } + + if (autoranging_) { + perform_autoranging(true, true); + + // It could be that a positive or negative div was added, so update + if (pvdiv_sb_) { + pvdiv_sb_->setValue(pos_vdivs_); + nvdiv_sb_->setValue(neg_vdivs_); + } + } if (owner_) { // Call order is important, otherwise the lazy event handler won't work @@ -472,10 +764,27 @@ void AnalogSignal::on_pos_vdivs_changed(int vdivs) void AnalogSignal::on_neg_vdivs_changed(int vdivs) { + if (vdivs == neg_vdivs_) + return; + neg_vdivs_ = vdivs; - if (autoranging_) - perform_autoranging(true); + // There has to be at least one div, positive or negative + if ((neg_vdivs_ == 0) && (pos_vdivs_ == 0)) { + pos_vdivs_ = 1; + if (pvdiv_sb_) + pvdiv_sb_->setValue(pos_vdivs_); + } + + if (autoranging_) { + perform_autoranging(true, true); + + // It could be that a positive or negative div was added, so update + if (pvdiv_sb_) { + pvdiv_sb_->setValue(pos_vdivs_); + nvdiv_sb_->setValue(neg_vdivs_); + } + } if (owner_) { // Call order is important, otherwise the lazy event handler won't work @@ -498,7 +807,30 @@ void AnalogSignal::on_autoranging_changed(int state) autoranging_ = (state == Qt::Checked); if (autoranging_) - perform_autoranging(true); + perform_autoranging(false, true); + + if (owner_) { + // Call order is important, otherwise the lazy event handler won't work + owner_->extents_changed(false, true); + owner_->row_item_appearance_changed(false, true); + } +} + +void AnalogSignal::on_conversion_changed(int index) +{ + data::SignalBase::ConversionType old_conv_type = conversion_type_; + + conversion_type_ = (data::SignalBase::ConversionType)(conversion_cb_->itemData(index).toInt()); + + if (conversion_type_ != old_conv_type) { + base_->set_conversion_type(conversion_type_); + update_conversion_type(); + } +} + +void AnalogSignal::on_display_type_changed(int index) +{ + display_type_ = (DisplayType)(display_type_cb_->itemData(index).toInt()); if (owner_) owner_->row_item_appearance_changed(false, true);