using std::deque;
using std::div;
using std::div_t;
-using std::isnan;
+// Note that "using std::isnan;" is _not_ put here since that would break
+// compilation on some platforms. Use "std::isnan()" instead in checks below.
using std::max;
using std::make_pair;
using std::min;
using std::shared_ptr;
using std::vector;
+using pv::data::LogicSegment;
using pv::data::SignalBase;
using pv::util::SIPrefix;
shared_ptr<data::SignalBase> base) :
Signal(session, base),
scale_index_(4), // 20 per div
- scale_index_drag_offset_(0),
pos_vdivs_(1),
neg_vdivs_(1),
resolution_(0),
display_type_(DisplayBoth),
- autoranging_(true)
+ autoranging_(true),
+ value_at_hover_pos_(std::numeric_limits<float>::quiet_NaN())
{
axis_pen_ = AxisPen;
connect(analog_data, SIGNAL(min_max_changed(float, float)),
this, SLOT(on_min_max_changed(float, float)));
- GlobalSettings gs;
+ GlobalSettings settings;
+ show_sampling_points_ =
+ settings.value(GlobalSettings::Key_View_ShowSamplingPoints).toBool();
+ fill_high_areas_ =
+ settings.value(GlobalSettings::Key_View_FillSignalHighAreas).toBool();
+ high_fill_color_ = QColor::fromRgba(settings.value(
+ GlobalSettings::Key_View_FillSignalHighAreaColor).value<uint32_t>());
+ show_analog_minor_grid_ =
+ settings.value(GlobalSettings::Key_View_ShowAnalogMinorGrid).toBool();
conversion_threshold_disp_mode_ =
- gs.value(GlobalSettings::Key_View_ConversionThresholdDispMode).toInt();
-
- div_height_ = gs.value(GlobalSettings::Key_View_DefaultDivHeight).toInt();
+ settings.value(GlobalSettings::Key_View_ConversionThresholdDispMode).toInt();
+ div_height_ = settings.value(GlobalSettings::Key_View_DefaultDivHeight).toInt();
base_->set_color(SignalColors[base_->index() % countof(SignalColors)]);
update_scale();
}
-AnalogSignal::~AnalogSignal()
-{
- GlobalSettings::remove_change_handler(this);
-}
-
shared_ptr<pv::data::SignalData> AnalogSignal::data() const
{
return base_->analog_data();
return make_pair(-ph, nh);
}
-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;
-}
-
-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);
-
- update_scale();
-}
-
-void AnalogSignal::scale_handle_drag_release()
-{
- scale_index_drag_offset_ = scale_index_;
- update_scale();
-}
-
-void AnalogSignal::on_setting_changed(const QString &key, const QVariant &value)
-{
- Signal::on_setting_changed(key, value);
-
- if (key == GlobalSettings::Key_View_ConversionThresholdDispMode)
- on_settingViewConversionThresholdDispMode_changed(value);
-}
-
void AnalogSignal::paint_back(QPainter &p, ViewItemPaintParams &pp)
{
if (!base_->enabled())
paint_axis(p, pp, get_visual_y());
} else {
- Trace::paint_back(p, pp);
+ Signal::paint_back(p, pp);
paint_axis(p, pp, get_visual_y());
}
}
// 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_ && !isnan(value_at_hover_pos_)) {
+ 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, 0, "V", false))
.arg(resolution_);
{
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++) {
}
}
- if ((pos_vdivs_ > 0) && show_analog_minor_grid) {
+ 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_;
}
}
- if ((pos_vdivs_ > 0) && show_analog_minor_grid) {
+ 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_;
// Calculate and paint the sampling points if enabled and useful
GlobalSettings settings;
const bool show_sampling_points =
- (settings.value(GlobalSettings::Key_View_ShowSamplingPoints).toBool() ||
- paint_thr_dots) && (samples_per_pixel < 0.25);
+ (show_sampling_points_ || paint_thr_dots) && (samples_per_pixel < 0.25);
p.setPen(base_->color());
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 float signal_height = low_offset - high_offset;
shared_ptr<pv::data::LogicSegment> segment = get_logic_segment_to_paint();
if (!segment || (segment->get_sample_count() == 0))
samples_per_pixel / LogicSignal::Oversampling, 0);
assert(edges.size() >= 2);
- // Check whether we need to paint the sampling points
- GlobalSettings settings;
- const bool show_sampling_points =
- settings.value(GlobalSettings::Key_View_ShowSamplingPoints).toBool() &&
- (samples_per_pixel < 0.25);
+ const float first_sample_x =
+ pp.left() + (edges.front().first / samples_per_pixel - pixels_offset);
+ const float last_sample_x =
+ pp.left() + (edges.back().first / samples_per_pixel - pixels_offset);
+ // Check whether we need to paint the sampling points
+ const bool show_sampling_points = show_sampling_points_ && (samples_per_pixel < 0.25);
vector<QRectF> sampling_points;
- float sampling_point_x = 0.0f;
+ float sampling_point_x = first_sample_x;
int64_t sampling_point_sample = start_sample;
const int w = 2;
- if (show_sampling_points) {
+ if (show_sampling_points)
sampling_points.reserve(end_sample - start_sample + 1);
- sampling_point_x = (edges.cbegin()->first / samples_per_pixel - pixels_offset) + pp.left();
- }
+
+ vector<QRectF> high_rects;
+ float rising_edge_x;
+ bool rising_edge_seen = false;
// Paint the edges
const unsigned int edge_count = edges.size() - 2;
QLineF *const edge_lines = new QLineF[edge_count];
line = edge_lines;
+ if (edges.front().second) {
+ // Beginning of trace is high
+ rising_edge_x = first_sample_x;
+ rising_edge_seen = true;
+ }
+
for (auto i = edges.cbegin() + 1; i != edges.cend() - 1; i++) {
- const float x = ((*i).first / samples_per_pixel -
- pixels_offset) + pp.left();
+ // Note: multiple edges occupying a single pixel are represented by an edge
+ // with undefined logic level. This means that only the first falling edge
+ // after a rising edge corresponds to said rising edge - and vice versa. If
+ // more edges with the same logic level follow, they denote multiple edges.
+
+ const float x = pp.left() + ((*i).first / samples_per_pixel - pixels_offset);
*line++ = QLineF(x, high_offset, x, low_offset);
+ if (fill_high_areas_) {
+ // Any edge terminates a high area
+ if (rising_edge_seen) {
+ const int width = x - rising_edge_x;
+ if (width > 0)
+ high_rects.emplace_back(rising_edge_x, high_offset,
+ width, signal_height);
+ rising_edge_seen = false;
+ }
+
+ // Only rising edges start high areas
+ if ((*i).second) {
+ rising_edge_x = x;
+ rising_edge_seen = true;
+ }
+ }
+
if (show_sampling_points)
while (sampling_point_sample < (*i).first) {
const float y = (*i).second ? low_offset : high_offset;
sampling_point_x += pixels_per_sample;
};
+ if (fill_high_areas_) {
+ // Add last high rectangle if the signal is still high at the end of the trace
+ if (rising_edge_seen && (edges.cend() - 1)->second)
+ high_rects.emplace_back(rising_edge_x, high_offset,
+ last_sample_x - rising_edge_x, signal_height);
+
+ p.setPen(high_fill_color_);
+ p.setBrush(high_fill_color_);
+ p.drawRects((const QRectF*)(high_rects.data()), high_rects.size());
+ }
+
p.setPen(LogicSignal::EdgeColor);
p.drawLines(edge_lines, edge_count);
delete[] edge_lines;
conv_threshold_cb_->blockSignals(true);
// Set available options depending on chosen conversion
- for (pair<QString, int> preset : presets)
+ for (pair<QString, int>& preset : presets)
conv_threshold_cb_->addItem(preset.first, preset.second);
map < QString, QVariant > options = base_->get_conversion_options();
conv_threshold_cb_->blockSignals(false);
}
+vector<data::LogicSegment::EdgePair> AnalogSignal::get_nearest_level_changes(uint64_t sample_pos)
+{
+ assert(base_);
+ assert(owner_);
+
+ // Return if there's no logic data or we're showing only the analog trace
+ if (!base_->logic_data() || (display_type_ == DisplayAnalog))
+ return vector<data::LogicSegment::EdgePair>();
+
+ if (sample_pos == 0)
+ return vector<LogicSegment::EdgePair>();
+
+ shared_ptr<LogicSegment> segment = get_logic_segment_to_paint();
+ if (!segment || (segment->get_sample_count() == 0))
+ return vector<LogicSegment::EdgePair>();
+
+ const View *view = owner_->view();
+ assert(view);
+ const double samples_per_pixel = base_->get_samplerate() * view->scale();
+
+ vector<LogicSegment::EdgePair> edges;
+
+ segment->get_surrounding_edges(edges, sample_pos,
+ samples_per_pixel / LogicSignal::Oversampling, 0);
+
+ if (edges.empty())
+ return vector<LogicSegment::EdgePair>();
+
+ return edges;
+}
+
void AnalogSignal::perform_autoranging(bool keep_divs, bool force_update)
{
const deque< shared_ptr<pv::data::AnalogSegment> > &segments =
static double prev_min = 0, prev_max = 0;
double min = 0, max = 0;
- for (shared_ptr<pv::data::AnalogSegment> segment : segments) {
+ for (const shared_ptr<pv::data::AnalogSegment>& segment : segments) {
pair<double, double> mm = segment->get_min_max();
min = std::min(min, mm.first);
max = std::max(max, mm.second);
{
// Note: NAN is used to indicate the non-existance of a value at this pixel
- if (isnan(prev_value_at_pixel_)) {
+ if (std::isnan(prev_value_at_pixel_)) {
if (x < 0) {
min_value_at_pixel_ = value;
max_value_at_pixel_ = value;
if (pixel_pos > current_pixel_pos_) {
if (pixel_pos - current_pixel_pos_ == 1) {
- if (isnan(prev_value_at_pixel_)) {
+ if (std::isnan(prev_value_at_pixel_)) {
value_at_pixel_pos_.push_back(prev_value_at_pixel_);
} else {
// Average the min/max range to create one value for the previous pixel
connect(conv_threshold_cb_, SIGNAL(currentIndexChanged(int)),
this, SLOT(on_conv_threshold_changed(int)));
- connect(conv_threshold_cb_, SIGNAL(editTextChanged(const QString)),
+ connect(conv_threshold_cb_, SIGNAL(editTextChanged(const QString&)),
this, SLOT(on_conv_threshold_changed())); // index will be -1
// Add the display type dropdown
} else {
try {
value_at_hover_pos_ = value_at_pixel_pos_.at(hp.x());
- } catch (out_of_range) {
+ } catch (out_of_range&) {
value_at_hover_pos_ = std::numeric_limits<float>::quiet_NaN();
}
}
}
+void AnalogSignal::on_setting_changed(const QString &key, const QVariant &value)
+{
+ Signal::on_setting_changed(key, value);
+
+ if (key == GlobalSettings::Key_View_ShowSamplingPoints)
+ show_sampling_points_ = value.toBool();
+
+ if (key == GlobalSettings::Key_View_FillSignalHighAreas)
+ fill_high_areas_ = value.toBool();
+
+ if (key == GlobalSettings::Key_View_FillSignalHighAreaColor)
+ high_fill_color_ = QColor::fromRgba(value.value<uint32_t>());
+
+ if (key == GlobalSettings::Key_View_ShowAnalogMinorGrid)
+ show_analog_minor_grid_ = value.toBool();
+
+ if (key == GlobalSettings::Key_View_ConversionThresholdDispMode) {
+ conversion_threshold_disp_mode_ = value.toInt();
+
+ if (owner_)
+ owner_->row_item_appearance_changed(false, true);
+ }
+}
+
void AnalogSignal::on_min_max_changed(float min, float max)
{
(void)min;
owner_->row_item_appearance_changed(false, true);
}
-void AnalogSignal::on_settingViewConversionThresholdDispMode_changed(const QVariant new_value)
-{
- conversion_threshold_disp_mode_ = new_value.toInt();
-
- if (owner_)
- owner_->row_item_appearance_changed(false, true);
-}
-
} // namespace trace
} // namespace views
} // namespace pv