samples_cleared();
}
+double Analog::get_samplerate() const
+{
+ if (segments_.empty())
+ return 1.0;
+
+ return segments_.front()->samplerate();
+}
+
uint64_t Analog::max_sample_count() const
{
uint64_t l = 0;
void clear();
+ double get_samplerate() const;
+
uint64_t max_sample_count() const;
void notify_samples_added(QObject* segment, uint64_t start_sample,
samples_cleared();
}
+double Logic::get_samplerate() const
+{
+ if (segments_.empty())
+ return 1.0;
+
+ return segments_.front()->samplerate();
+}
+
uint64_t Logic::max_sample_count() const
{
uint64_t l = 0;
void clear();
+ double get_samplerate() const;
+
uint64_t max_sample_count() const;
void notify_samples_added(QObject* segment, uint64_t start_sample,
return result;
}
+double SignalBase::get_samplerate() const
+{
+ if (channel_type_ == AnalogChannel)
+ {
+ shared_ptr<Analog> data = dynamic_pointer_cast<Analog>(data_);
+ if (data)
+ return data->get_samplerate();
+ }
+
+ if (channel_type_ == LogicChannel)
+ {
+ shared_ptr<Logic> data = dynamic_pointer_cast<Logic>(data_);
+ if (data)
+ return data->get_samplerate();
+ }
+
+ // Default samplerate is 1 Hz
+ return 1.0;
+}
+
SignalBase::ConversionType SignalBase::get_conversion_type() const
{
return conversion_type_;
*/
bool has_samples() const;
+ /**
+ * Returns the sample rate for this signal.
+ */
+ double get_samplerate() const;
+
/**
* Queries the kind of conversion performed on this channel.
*/
virtual void clear() = 0;
virtual uint64_t max_sample_count() const = 0;
+
+ virtual double get_samplerate() const = 0;
};
} // namespace data
using std::shared_ptr;
using std::vector;
+using pv::data::LogicSegment;
using pv::data::SignalBase;
using pv::util::SIPrefix;
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 =
void update_conversion_widgets();
+ /**
+ * Determines the closest level change (i.e. edge) to a given sample, which
+ * is useful for e.g. the "snap to edge" functionality.
+ *
+ * @param sample_pos Sample to use
+ * @return The changes left and right of the given position
+ */
+ virtual vector<data::LogicSegment::EdgePair> get_nearest_level_changes(uint64_t sample_pos);
+
void perform_autoranging(bool keep_divs, bool force_update);
void reset_pixel_values();
using sigrok::TriggerMatch;
using sigrok::TriggerMatchType;
+using pv::data::LogicSegment;
+
namespace pv {
namespace views {
namespace trace {
const float high_offset = y - signal_height_ + 0.5f;
const float low_offset = y + 0.5f;
- shared_ptr<pv::data::LogicSegment> segment = get_logic_segment_to_paint();
+ shared_ptr<LogicSegment> segment = get_logic_segment_to_paint();
if (!segment || (segment->get_sample_count() == 0))
return;
}
}
-void LogicSignal::hover_point_changed(const QPoint &hp)
+vector<LogicSegment::EdgePair> LogicSignal::get_nearest_level_changes(uint64_t sample_pos)
{
- Signal::hover_point_changed(hp);
-
assert(base_);
assert(owner_);
- if ((!base_->enabled()) || (hp.x() == 0))
- return;
-
- // Ignore if mouse cursor is not hovering over this trace
- const int y = get_visual_y();
- const pair<int, int> extents = v_extents();
- if ((hp.y() < (y + extents.first)) || ((hp.y() > (y + extents.second))))
- return;
+ if (sample_pos == 0)
+ return vector<LogicSegment::EdgePair>();
- shared_ptr<pv::data::LogicSegment> segment = get_logic_segment_to_paint();
+ shared_ptr<LogicSegment> segment = get_logic_segment_to_paint();
if (!segment || (segment->get_sample_count() == 0))
- return;
-
- double samplerate = segment->samplerate();
-
- // Show sample rate as 1Hz when it is unknown
- if (samplerate == 0.0)
- samplerate = 1.0;
+ return vector<LogicSegment::EdgePair>();
const View *view = owner_->view();
assert(view);
- const double scale = view->scale();
- const double pixels_offset =
- ((view->offset() - segment->start_time()) / scale).convert_to<double>();
- const double samples_per_pixel = samplerate * scale;
-
- const uint64_t sample_pos = (uint64_t)max(
- (hp.x() + pixels_offset) * samples_per_pixel, 0.0);
+ const double samples_per_pixel = base_->get_samplerate() * view->scale();
- vector<data::LogicSegment::EdgePair> edges;
+ vector<LogicSegment::EdgePair> edges;
segment->get_surrounding_edges(edges, sample_pos,
samples_per_pixel / Oversampling, base_->index());
if (edges.empty())
- return;
+ return vector<LogicSegment::EdgePair>();
+
+ return edges;
}
void LogicSignal::paint_caps(QPainter &p, QLineF *const lines,
*/
virtual void paint_fore(QPainter &p, ViewItemPaintParams &pp);
- virtual void hover_point_changed(const QPoint &hp);
+ /**
+ * Determines the closest level change (i.e. edge) to a given sample, which
+ * is useful for e.g. the "snap to edge" functionality.
+ *
+ * @param sample_pos Sample to use
+ * @return The changes left and right of the given position
+ */
+ virtual vector<data::LogicSegment::EdgePair> get_nearest_level_changes(uint64_t sample_pos);
private:
void paint_caps(QPainter &p, QLineF *const lines,
#include <cstdint>
+#include <pv/data/logicsegment.hpp>
+
#include "trace.hpp"
#include "viewitemowner.hpp"
virtual shared_ptr<pv::data::SignalData> data() const = 0;
+ /**
+ * Determines the closest level change (i.e. edge) to a given sample, which
+ * is useful for e.g. the "snap to edge" functionality.
+ *
+ * @param sample_pos Sample to use
+ * @return The changes left and right of the given position
+ */
+ virtual vector<data::LogicSegment::EdgePair> get_nearest_level_changes(uint64_t sample_pos) = 0;
+
/**
* Returns true if the trace is visible and enabled.
*/
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "signal.hpp"
#include "timeitem.hpp"
#include "view.hpp"
void TimeItem::drag_by(const QPoint &delta)
{
- set_time(view_.offset() + (drag_point_.x() + delta.x() - 0.5) *
- view_.scale());
+ int64_t sample_num = view_.get_nearest_level_change(drag_point_ + delta);
+
+ if (sample_num > -1)
+ set_time(sample_num / view_.get_signal_under_mouse_cursor()->base()->get_samplerate());
+ else
+ set_time(view_.offset() + (drag_point_.x() + delta.x() - 0.5) *
+ view_.scale());
}
} // namespace trace
QPoint TimeMarker::drag_point(const QRect &rect) const
{
- return QPoint(get_x(), rect.bottom());
+ (void)rect;
+
+ return QPoint(get_x(), view_.mapFromGlobal(QCursor::pos()).y());
}
QRectF TimeMarker::label_rect(const QRectF &rect) const
}
#endif
+shared_ptr<Signal> View::get_signal_under_mouse_cursor() const
+{
+ return signal_under_mouse_cursor_;
+}
+
View* View::view()
{
return this;
return hover_point_;
}
+int64_t View::get_nearest_level_change(const QPoint &p) const
+{
+ shared_ptr<Signal> signal = signal_under_mouse_cursor_;
+
+ if (!signal)
+ return -1;
+
+ // Calculate sample number from cursor position
+ const double samples_per_pixel = signal->base()->get_samplerate() * scale();
+ const int64_t x_offset = offset().convert_to<double>() / scale();
+ const int64_t sample_num = max(((x_offset + p.x()) * samples_per_pixel), 0.0);
+
+ // Query for nearest level changes
+ vector<data::LogicSegment::EdgePair> edges =
+ signal->get_nearest_level_changes(sample_num);
+
+ if (edges.size() != 2)
+ return -1;
+
+ // We received absolute sample numbers, make them relative
+ const int64_t left_sample_delta = sample_num - edges.front().first;
+ const int64_t right_sample_delta = edges.back().first - sample_num - 1;
+
+ const int64_t left_delta = left_sample_delta / samples_per_pixel;
+ const int64_t right_delta = right_sample_delta / samples_per_pixel;
+
+ int64_t nearest = -1;
+
+ // Only use closest left or right edge if they're close to the cursor
+ if ((left_delta < right_delta) && (left_delta < 15))
+ nearest = edges.front().first;
+ if ((left_delta >= right_delta) && (right_delta < 15))
+ nearest = edges.back().first;
+
+ return nearest;
+}
+
void View::restack_all_trace_tree_items()
{
// Make a list of owners that is sorted from deepest first
void View::update_hover_point()
{
+ // Determine signal that the mouse cursor is hovering over
+ signal_under_mouse_cursor_.reset();
+ for (shared_ptr<Signal> s : signals_) {
+ const pair<int, int> extents = s->v_extents();
+ const int top = s->get_visual_y() + extents.first;
+ const int btm = s->get_visual_y() + extents.second;
+ if ((hover_point_.y() >= top) && (hover_point_.y() <= btm)
+ && s->base()->enabled())
+ signal_under_mouse_cursor_ = s;
+ }
+
+ // Update all trace tree items
const vector<shared_ptr<TraceTreeItem>> trace_tree_items(
list_by_type<TraceTreeItem>());
for (shared_ptr<TraceTreeItem> r : trace_tree_items)
r->hover_point_changed(hover_point_);
+ // Notify any other listeners
hover_point_changed(hover_point_);
}
virtual void remove_decode_signal(shared_ptr<data::DecodeSignal> signal);
#endif
+ shared_ptr<Signal> get_signal_under_mouse_cursor() const;
+
/**
* Returns the view of the owner.
*/
const QPoint& hover_point() const;
+ /**
+ * Determines the closest level change (i.e. edge) to a given point, which
+ * is useful for e.g. the "snap to edge" functionality.
+ *
+ * @param p The current position of the mouse cursor
+ * @return The sample number of the nearest level change or -1 if none
+ */
+ int64_t get_nearest_level_change(const QPoint &p) const;
+
void restack_all_trace_tree_items();
int header_width() const;
vector< shared_ptr<TriggerMarker> > trigger_markers_;
QPoint hover_point_;
+ shared_ptr<Signal> signal_under_mouse_cursor_;
unsigned int sticky_events_;
QTimer lazy_event_handler_;