forked from Ivasoft/DSView
1398 lines
35 KiB
C++
1398 lines
35 KiB
C++
/*
|
|
* This file is part of the DSView project.
|
|
* DSView is based on PulseView.
|
|
*
|
|
* Copyright (C) 2012 Joel Holdsworth <joel@airwebreathe.org.uk>
|
|
* Copyright (C) 2013 DreamSourceLab <support@dreamsourcelab.com>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
#include <assert.h>
|
|
#include <limits.h>
|
|
#include <cmath>
|
|
|
|
#include <QEvent>
|
|
#include <QMouseEvent>
|
|
#include <QScrollBar>
|
|
|
|
#include "groupsignal.h"
|
|
#include "decodetrace.h"
|
|
#include "header.h"
|
|
#include "devmode.h"
|
|
#include "ruler.h"
|
|
#include "signal.h"
|
|
#include "dsosignal.h"
|
|
#include "view.h"
|
|
#include "viewport.h"
|
|
#include "spectrumtrace.h"
|
|
#include "lissajoustrace.h"
|
|
#include "analogsignal.h"
|
|
|
|
#include "../device/devinst.h"
|
|
#include "../sigsession.h"
|
|
#include "../data/logic.h"
|
|
#include "../data/logicsnapshot.h"
|
|
#include "../dialogs/calibration.h"
|
|
#include "../dialogs/lissajousoptions.h"
|
|
#include "../dsvdef.h"
|
|
|
|
|
|
using namespace std;
|
|
|
|
namespace pv {
|
|
namespace view {
|
|
|
|
const int View::LabelMarginWidth = 70;
|
|
const int View::RulerHeight = 50;
|
|
|
|
const int View::MaxScrollValue = INT_MAX / 2;
|
|
const int View::MaxHeightUnit = 20;
|
|
|
|
//const int View::SignalHeight = 30;s
|
|
const int View::SignalMargin = 7;
|
|
const int View::SignalSnapGridSize = 10;
|
|
|
|
const QColor View::CursorAreaColour(220, 231, 243);
|
|
const QSizeF View::LabelPadding(4, 4);
|
|
const QString View::Unknown_Str = "########";
|
|
|
|
const QColor View::Red = QColor(213, 15, 37, 255);
|
|
const QColor View::Orange = QColor(238, 178, 17, 255);
|
|
const QColor View::Blue = QColor(17, 133, 209, 255);
|
|
const QColor View::Green = QColor(0, 153, 37, 255);
|
|
const QColor View::Purple = QColor(109, 50, 156, 255);
|
|
const QColor View::LightBlue = QColor(17, 133, 209, 200);
|
|
const QColor View::LightRed = QColor(213, 15, 37, 200);
|
|
|
|
|
|
View::View(SigSession *session, pv::toolbars::SamplingBar *sampling_bar, QWidget *parent) :
|
|
QScrollArea(parent),
|
|
_sampling_bar(sampling_bar),
|
|
_scale(10),
|
|
_preScale(1e-6),
|
|
_maxscale(1e9),
|
|
_minscale(1e-15),
|
|
_offset(0),
|
|
_preOffset(0),
|
|
_updating_scroll(false),
|
|
_trig_hoff(0),
|
|
_show_cursors(false),
|
|
_search_hit(false),
|
|
_show_xcursors(false),
|
|
_hover_point(-1, -1),
|
|
_dso_auto(true),
|
|
_show_lissajous(false),
|
|
_back_ready(false)
|
|
{
|
|
_trig_cursor = NULL;
|
|
_search_cursor = NULL;
|
|
|
|
_session = session;
|
|
_device_agent = session->get_device();
|
|
session->add_msg_listener(this);
|
|
|
|
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
|
|
|
|
// trace viewport map
|
|
_trace_view_map[SR_CHANNEL_LOGIC] = TIME_VIEW;
|
|
_trace_view_map[SR_CHANNEL_GROUP] = TIME_VIEW;
|
|
_trace_view_map[SR_CHANNEL_DECODER] = TIME_VIEW;
|
|
_trace_view_map[SR_CHANNEL_ANALOG] = TIME_VIEW;
|
|
_trace_view_map[SR_CHANNEL_DSO] = TIME_VIEW;
|
|
_trace_view_map[SR_CHANNEL_FFT] = FFT_VIEW;
|
|
_trace_view_map[SR_CHANNEL_LISSAJOUS] = TIME_VIEW;
|
|
_trace_view_map[SR_CHANNEL_MATH] = TIME_VIEW;
|
|
|
|
_active_viewport = NULL;
|
|
_ruler = new Ruler(*this);
|
|
_header = new Header(*this);
|
|
_devmode = new DevMode(this, session);
|
|
|
|
setViewportMargins(headerWidth(), RulerHeight, 0, 0);
|
|
|
|
// windows splitter
|
|
_time_viewport = new Viewport(*this, TIME_VIEW);
|
|
_time_viewport->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
|
|
_time_viewport->setMinimumHeight(100);
|
|
|
|
_fft_viewport = new Viewport(*this, FFT_VIEW);
|
|
_fft_viewport->setVisible(false);
|
|
_fft_viewport->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
|
|
_fft_viewport->setMinimumHeight(100);
|
|
|
|
_vsplitter = new QSplitter(this);
|
|
_vsplitter->setOrientation(Qt::Vertical);
|
|
_vsplitter->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
|
|
|
|
_viewport_list.push_back(_time_viewport);
|
|
_vsplitter->addWidget(_time_viewport);
|
|
_vsplitter->setCollapsible(0, false);
|
|
_vsplitter->setStretchFactor(0, 2);
|
|
_viewport_list.push_back(_fft_viewport);
|
|
_vsplitter->addWidget(_fft_viewport);
|
|
_vsplitter->setCollapsible(1, false);
|
|
_vsplitter->setStretchFactor(1, 1);
|
|
|
|
_viewcenter = new QWidget(this);
|
|
_viewcenter->setContentsMargins(0,0,0,0);
|
|
QGridLayout* layout = new QGridLayout(_viewcenter);
|
|
layout->setSpacing(0);
|
|
layout->setContentsMargins(0,0,0,0);
|
|
_viewcenter->setLayout(layout);
|
|
layout->addWidget(_vsplitter, 0, 0);
|
|
_viewbottom = new ViewStatus(_session, *this);
|
|
_viewbottom->setFixedHeight(StatusHeight);
|
|
layout->addWidget(_viewbottom, 1, 0);
|
|
setViewport(_viewcenter);
|
|
|
|
_time_viewport->installEventFilter(this);
|
|
_fft_viewport->installEventFilter(this);
|
|
_ruler->installEventFilter(this);
|
|
_header->installEventFilter(this);
|
|
_devmode->installEventFilter(this);
|
|
|
|
_viewcenter->setObjectName(tr("ViewArea_center"));
|
|
_ruler->setObjectName(tr("ViewArea_ruler"));
|
|
_header->setObjectName(tr("ViewArea_header"));
|
|
|
|
QColor fore(QWidget::palette().color(QWidget::foregroundRole()));
|
|
fore.setAlpha(View::BackAlpha);
|
|
|
|
_show_trig_cursor = false;
|
|
_trig_cursor = new Cursor(*this, View::LightRed, 0);
|
|
_show_search_cursor = false;
|
|
_search_pos = 0;
|
|
_search_cursor = new Cursor(*this, fore, _search_pos);
|
|
|
|
_cali = new pv::dialogs::Calibration(this);
|
|
_cali->hide();
|
|
|
|
connect(horizontalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(h_scroll_value_changed(int)));
|
|
connect(verticalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(v_scroll_value_changed(int)));
|
|
|
|
connect(_time_viewport, SIGNAL(measure_updated()),this, SLOT(on_measure_updated()));
|
|
connect(_time_viewport, SIGNAL(prgRate(int)), this, SIGNAL(prgRate(int)));
|
|
connect(_fft_viewport, SIGNAL(measure_updated()), this, SLOT(on_measure_updated()));
|
|
|
|
connect(_vsplitter, SIGNAL(splitterMoved(int,int)), this, SLOT(splitterMoved(int, int)));
|
|
|
|
connect(_header, SIGNAL(traces_moved()),this, SLOT(on_traces_moved()));
|
|
connect(_header, SIGNAL(header_updated()),this, SLOT(header_updated()));
|
|
}
|
|
|
|
View::~View(){
|
|
DESTROY_OBJECT(_trig_cursor);
|
|
DESTROY_OBJECT(_search_cursor);
|
|
}
|
|
|
|
void View::show_wait_trigger()
|
|
{
|
|
_time_viewport->show_wait_trigger();
|
|
}
|
|
|
|
void View::set_device()
|
|
{
|
|
_devmode->set_device();
|
|
}
|
|
|
|
SigSession& View::session()
|
|
{
|
|
return *_session;
|
|
}
|
|
|
|
double View::scale()
|
|
{
|
|
return _scale;
|
|
}
|
|
|
|
int64_t View::offset()
|
|
{
|
|
return _offset;
|
|
}
|
|
|
|
double View::trig_hoff()
|
|
{
|
|
return _trig_hoff;
|
|
}
|
|
|
|
void View::set_trig_hoff(double hoff)
|
|
{
|
|
_trig_hoff = hoff;
|
|
}
|
|
|
|
double View::get_minscale()
|
|
{
|
|
return _minscale;
|
|
}
|
|
|
|
double View::get_maxscale()
|
|
{
|
|
return _maxscale;
|
|
}
|
|
|
|
void View::capture_init()
|
|
{
|
|
int mode = _device_agent->get_work_mode();
|
|
|
|
if (mode == DSO)
|
|
show_trig_cursor(true);
|
|
else if (!_session->isRepeating())
|
|
show_trig_cursor(false);
|
|
|
|
_maxscale = _session->cur_sampletime() / (get_view_width() * MaxViewRate);
|
|
if (mode == ANALOG)
|
|
set_scale_offset(_maxscale, 0);
|
|
status_clear();
|
|
_trig_time_setted = false;
|
|
_trig_hoff = 0;
|
|
}
|
|
|
|
void View::zoom(double steps)
|
|
{
|
|
zoom(steps, get_view_width() / 2);
|
|
}
|
|
|
|
void View::set_update(Viewport *viewport, bool need_update)
|
|
{
|
|
viewport->set_need_update(need_update);
|
|
}
|
|
|
|
void View::set_all_update(bool need_update)
|
|
{
|
|
_time_viewport->set_need_update(need_update);
|
|
_fft_viewport->set_need_update(need_update);
|
|
}
|
|
|
|
double View::get_hori_res()
|
|
{
|
|
return _sampling_bar->get_hori_res();
|
|
}
|
|
|
|
void View::update_hori_res()
|
|
{
|
|
if (_device_agent->get_work_mode() == DSO) {
|
|
_sampling_bar->hori_knob(0);
|
|
}
|
|
}
|
|
|
|
bool View::zoom(double steps, int offset)
|
|
{
|
|
bool ret = true;
|
|
_preScale = _scale;
|
|
_preOffset = _offset;
|
|
|
|
if (_device_agent->get_work_mode() != DSO) {
|
|
_scale *= std::pow(3.0/2.0, -steps);
|
|
_scale = max(min(_scale, _maxscale), _minscale);
|
|
} else {
|
|
if (_session->get_capture_state() == SigSession::Running &&
|
|
_session->get_instant())
|
|
return ret;
|
|
|
|
double hori_res = -1;
|
|
if(steps > 0.5)
|
|
hori_res = _sampling_bar->hori_knob(-1);
|
|
else if (steps < -0.5)
|
|
hori_res = _sampling_bar->hori_knob(1);
|
|
|
|
if (hori_res > 0) {
|
|
const double scale = _session->cur_view_time() / get_view_width();
|
|
_scale = max(min(scale, _maxscale), _minscale);
|
|
} else {
|
|
ret = false;
|
|
}
|
|
}
|
|
|
|
_offset = floor((_offset + offset) * (_preScale / _scale) - offset);
|
|
_offset = max (min(_offset, get_max_offset()), get_min_offset());
|
|
|
|
if (_scale != _preScale || _offset != _preOffset) {
|
|
_header->update();
|
|
_ruler->update();
|
|
viewport_update();
|
|
update_scroll();
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
void View::timebase_changed()
|
|
{
|
|
if (_device_agent->get_work_mode() != DSO)
|
|
return;
|
|
|
|
double scale = this->scale();
|
|
double hori_res = _sampling_bar->get_hori_res();
|
|
if (hori_res > 0)
|
|
scale = _session->cur_view_time() / get_view_width();
|
|
set_scale_offset(scale, this->offset());
|
|
}
|
|
|
|
void View::set_scale_offset(double scale, int64_t offset)
|
|
{
|
|
//if (_session->get_capture_state() == SigSession::Stopped) {
|
|
_preScale = _scale;
|
|
_preOffset = _offset;
|
|
|
|
_scale = max(min(scale, _maxscale), _minscale);
|
|
_offset = floor(max(min(offset, get_max_offset()), get_min_offset()));
|
|
|
|
if (_scale != _preScale || _offset != _preOffset) {
|
|
update_scroll();
|
|
_header->update();
|
|
_ruler->update();
|
|
viewport_update();
|
|
}
|
|
//}
|
|
}
|
|
|
|
void View::set_preScale_preOffset()
|
|
{
|
|
//assert(_preScale <= _maxscale);
|
|
//assert(_preScale >= _minscale);
|
|
//assert(_preOffset >= 0);
|
|
|
|
set_scale_offset(_preScale, _preOffset);
|
|
}
|
|
|
|
std::vector<Trace*> View::get_traces(int type)
|
|
{
|
|
assert(_session);
|
|
|
|
auto &sigs = _session->get_signals();
|
|
const auto &groups = _session->get_group_signals();
|
|
|
|
const auto &decode_sigs = _session->get_decode_signals();
|
|
|
|
const auto &spectrums = _session->get_spectrum_traces();
|
|
|
|
std::vector<Trace*> traces;
|
|
for(auto &t : sigs) {
|
|
if (type == ALL_VIEW || _trace_view_map[t->get_type()] == type)
|
|
traces.push_back(t);
|
|
}
|
|
|
|
for(auto &t : decode_sigs) {
|
|
if (type == ALL_VIEW || _trace_view_map[t->get_type()] == type)
|
|
traces.push_back(t);
|
|
}
|
|
|
|
for(auto &t : groups) {
|
|
if (type == ALL_VIEW || _trace_view_map[t->get_type()] == type)
|
|
traces.push_back(t);
|
|
}
|
|
|
|
for(auto &t : spectrums) {
|
|
if (type == ALL_VIEW || _trace_view_map[t->get_type()] == type)
|
|
traces.push_back(t);
|
|
}
|
|
|
|
auto lissajous = _session->get_lissajous_trace();
|
|
if (lissajous && lissajous->enabled() &&
|
|
(type == ALL_VIEW || _trace_view_map[lissajous->get_type()] == type))
|
|
traces.push_back(lissajous);
|
|
|
|
auto math = _session->get_math_trace();
|
|
if (math && math->enabled() &&
|
|
(type == ALL_VIEW || _trace_view_map[math->get_type()] == type))
|
|
traces.push_back(math);
|
|
|
|
stable_sort(traces.begin(), traces.end(), compare_trace_v_offsets);
|
|
return traces;
|
|
}
|
|
|
|
bool View::compare_trace_v_offsets(const Trace *a,
|
|
const Trace *b)
|
|
{
|
|
assert(a);
|
|
assert(b);
|
|
|
|
Trace *a1 = const_cast<Trace*>(a);
|
|
Trace *b1 = const_cast<Trace*>(b);
|
|
|
|
if (a1->get_type() != b1->get_type())
|
|
return a1->get_type() < b1->get_type();
|
|
else if (a1->get_type() == SR_CHANNEL_DSO || a1->get_type() == SR_CHANNEL_ANALOG)
|
|
return a1->get_index() < b1->get_index();
|
|
else
|
|
return a1->get_v_offset() < b1->get_v_offset();
|
|
}
|
|
|
|
bool View::cursors_shown()
|
|
{
|
|
return _show_cursors;
|
|
}
|
|
|
|
bool View::trig_cursor_shown()
|
|
{
|
|
return _show_trig_cursor;
|
|
}
|
|
|
|
bool View::search_cursor_shown()
|
|
{
|
|
return _show_search_cursor;
|
|
}
|
|
|
|
void View::show_cursors(bool show)
|
|
{
|
|
_show_cursors = show;
|
|
_ruler->update();
|
|
viewport_update();
|
|
}
|
|
|
|
void View::show_trig_cursor(bool show)
|
|
{
|
|
_show_trig_cursor = show;
|
|
_ruler->update();
|
|
viewport_update();
|
|
}
|
|
|
|
void View::show_search_cursor(bool show)
|
|
{
|
|
_show_search_cursor = show;
|
|
_ruler->update();
|
|
viewport_update();
|
|
}
|
|
|
|
void View::status_clear()
|
|
{
|
|
_time_viewport->clear_dso_xm();
|
|
_time_viewport->clear_measure();
|
|
_viewbottom->clear();
|
|
}
|
|
|
|
void View::repeat_unshow()
|
|
{
|
|
_viewbottom->repeat_unshow();
|
|
}
|
|
|
|
void View::frame_began()
|
|
{
|
|
// if (_device_agent->get_work_mode() == LOGIC)
|
|
// _viewbottom->set_trig_time(_session->get_session_time());
|
|
_search_hit = false;
|
|
_search_pos = 0;
|
|
set_search_pos(_search_pos, _search_hit);
|
|
}
|
|
|
|
void View::set_trig_time()
|
|
{
|
|
if (!_trig_time_setted && _device_agent->get_work_mode() == LOGIC) {
|
|
_session->set_session_time(QDateTime::currentDateTime());
|
|
_viewbottom->set_trig_time(_session->get_session_time());
|
|
}
|
|
_trig_time_setted = true;
|
|
}
|
|
|
|
bool View::trig_time_setted()
|
|
{
|
|
return _trig_time_setted;
|
|
}
|
|
|
|
void View::receive_end()
|
|
{
|
|
if (_device_agent->get_work_mode() == LOGIC) {
|
|
GVariant *gvar = _device_agent->get_config(NULL, NULL, SR_CONF_RLE);
|
|
if (gvar != NULL) {
|
|
bool rle = g_variant_get_boolean(gvar);
|
|
g_variant_unref(gvar);
|
|
if (rle) {
|
|
gvar = _device_agent->get_config(NULL, NULL, SR_CONF_ACTUAL_SAMPLES);
|
|
if (gvar != NULL) {
|
|
uint64_t actual_samples = g_variant_get_uint64(gvar);
|
|
g_variant_unref(gvar);
|
|
if (actual_samples != _session->cur_samplelimits()) {
|
|
_viewbottom->set_rle_depth(actual_samples);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
_time_viewport->unshow_wait_trigger();
|
|
}
|
|
|
|
void View::receive_trigger(quint64 trig_pos)
|
|
{
|
|
const double time = trig_pos * 1.0 / _session->cur_snap_samplerate();
|
|
_trig_cursor->set_index(trig_pos);
|
|
if (ds_trigger_get_en() ||
|
|
_device_agent->name() == "virtual-session" ||
|
|
_device_agent->get_work_mode() == DSO) {
|
|
_show_trig_cursor = true;
|
|
set_scale_offset(_scale, (time / _scale) - (get_view_width() / 2));
|
|
}
|
|
|
|
_ruler->update();
|
|
viewport_update();
|
|
}
|
|
|
|
void View::set_trig_pos(int percent)
|
|
{
|
|
uint64_t index = _session->cur_samplelimits() * percent / 100;
|
|
receive_trigger(index);
|
|
}
|
|
|
|
void View::set_search_pos(uint64_t search_pos, bool hit)
|
|
{
|
|
//assert(search_pos >= 0);
|
|
QColor fore(QWidget::palette().color(QWidget::foregroundRole()));
|
|
fore.setAlpha(View::BackAlpha);
|
|
|
|
const double time = search_pos * 1.0 / _session->cur_snap_samplerate();
|
|
_search_pos = search_pos;
|
|
_search_hit = hit;
|
|
_search_cursor->set_index(search_pos);
|
|
_search_cursor->set_colour(hit ? View::Blue : fore);
|
|
|
|
if (hit) {
|
|
set_scale_offset(_scale, (time / _scale) - (get_view_width() / 2));
|
|
_ruler->update();
|
|
viewport_update();
|
|
}
|
|
}
|
|
|
|
uint64_t View::get_search_pos()
|
|
{
|
|
return _search_pos;
|
|
}
|
|
|
|
bool View::get_search_hit()
|
|
{
|
|
return _search_hit;
|
|
}
|
|
|
|
const QPoint& View::hover_point()
|
|
{
|
|
return _hover_point;
|
|
}
|
|
|
|
void View::normalize_layout()
|
|
{
|
|
auto traces = get_traces(ALL_VIEW);
|
|
|
|
int v_min = INT_MAX;
|
|
for(auto &t : traces){
|
|
v_min = min(t->get_v_offset(), v_min);
|
|
}
|
|
|
|
const int delta = -min(v_min, 0);
|
|
|
|
for(auto &t : traces){
|
|
t->set_v_offset(t->get_v_offset() + delta);
|
|
}
|
|
|
|
verticalScrollBar()->setSliderPosition(delta);
|
|
v_scroll_value_changed(verticalScrollBar()->sliderPosition());
|
|
}
|
|
|
|
|
|
int View::get_spanY()
|
|
{
|
|
return _spanY;
|
|
}
|
|
|
|
int View::get_signalHeight()
|
|
{
|
|
return _signalHeight;
|
|
}
|
|
|
|
void View::get_scroll_layout(int64_t &length, int64_t &offset)
|
|
{
|
|
const auto data_set = _session->get_data();
|
|
if (data_set.empty())
|
|
return;
|
|
|
|
length = ceil(_session->cur_snap_sampletime() / _scale);
|
|
offset = _offset;
|
|
}
|
|
|
|
void View::update_scroll()
|
|
{
|
|
assert(_viewcenter);
|
|
|
|
const QSize areaSize = QSize(get_view_width(), get_view_height());
|
|
|
|
// Set the horizontal scroll bar
|
|
int64_t length = 0;
|
|
int64_t offset = 0;
|
|
get_scroll_layout(length, offset);
|
|
length = max(length - areaSize.width(), (int64_t)0);
|
|
|
|
horizontalScrollBar()->setPageStep(areaSize.width() / 2);
|
|
|
|
_updating_scroll = true;
|
|
|
|
if (length < MaxScrollValue) {
|
|
horizontalScrollBar()->setRange(0, length);
|
|
horizontalScrollBar()->setSliderPosition(offset);
|
|
} else {
|
|
horizontalScrollBar()->setRange(0, MaxScrollValue);
|
|
horizontalScrollBar()->setSliderPosition(
|
|
_offset * 1.0 / length * MaxScrollValue);
|
|
}
|
|
|
|
_updating_scroll = false;
|
|
|
|
// Set the vertical scrollbar
|
|
verticalScrollBar()->setPageStep(areaSize.height());
|
|
verticalScrollBar()->setRange(0,0);
|
|
}
|
|
|
|
void View::update_scale_offset()
|
|
{
|
|
if (_device_agent->get_work_mode() != DSO) {
|
|
_maxscale = _session->cur_sampletime() / (get_view_width() * MaxViewRate);
|
|
_minscale = (1.0 / _session->cur_snap_samplerate()) / MaxPixelsPerSample;
|
|
} else {
|
|
_scale = _session->cur_view_time() / get_view_width();
|
|
_maxscale = 1e9;
|
|
_minscale = 1e-15;
|
|
}
|
|
|
|
_scale = max(min(_scale, _maxscale), _minscale);
|
|
_offset = max(min(_offset, get_max_offset()), get_min_offset());
|
|
|
|
_preScale = _scale;
|
|
_preOffset = _offset;
|
|
|
|
//_trig_cursor->set_index(_session->get_trigger_pos());
|
|
|
|
_ruler->update();
|
|
viewport_update();
|
|
}
|
|
|
|
void View::mode_changed()
|
|
{
|
|
if (_device_agent->name().contains("virtual"))
|
|
_scale = WellSamplesPerPixel * 1.0 / _session->cur_snap_samplerate();
|
|
_scale = max(min(_scale, _maxscale), _minscale);
|
|
}
|
|
|
|
void View::signals_changed()
|
|
{
|
|
double actualMargin = SignalMargin;
|
|
int total_rows = 0;
|
|
int label_size = 0;
|
|
uint8_t max_height = MaxHeightUnit;
|
|
std::vector<Trace*> time_traces;
|
|
std::vector<Trace*> fft_traces;
|
|
|
|
for(auto &t : get_traces(ALL_VIEW)) {
|
|
if (_trace_view_map[t->get_type()] == TIME_VIEW)
|
|
time_traces.push_back(t);
|
|
else if (_trace_view_map[t->get_type()] == FFT_VIEW)
|
|
if (t->enabled())
|
|
fft_traces.push_back(t);
|
|
}
|
|
|
|
if (!fft_traces.empty()) {
|
|
if (!_fft_viewport->isVisible()) {
|
|
_fft_viewport->setVisible(true);
|
|
_fft_viewport->clear_measure();
|
|
_viewport_list.push_back(_fft_viewport);
|
|
_vsplitter->refresh();
|
|
}
|
|
for(auto &t : fft_traces) {
|
|
t->set_view(this);
|
|
t->set_viewport(_fft_viewport);
|
|
t->set_totalHeight(_fft_viewport->height());
|
|
t->set_v_offset(_fft_viewport->geometry().bottom());
|
|
}
|
|
} else {
|
|
_fft_viewport->setVisible(false);
|
|
_vsplitter->refresh();
|
|
|
|
// Find the _fft_viewport in the stack
|
|
std::list< QWidget *>::iterator iter = _viewport_list.begin();
|
|
for(unsigned int i = 0; i < _viewport_list.size(); i++, iter++)
|
|
if ((*iter) == _fft_viewport)
|
|
break;
|
|
// Delete the element
|
|
if (iter != _viewport_list.end())
|
|
_viewport_list.erase(iter);
|
|
}
|
|
|
|
if (!time_traces.empty() && _time_viewport) {
|
|
for(auto &t : time_traces) {
|
|
assert(t);
|
|
if (dynamic_cast<DsoSignal*>(t) ||
|
|
t->enabled())
|
|
total_rows += t->rows_size();
|
|
if (t->rows_size() != 0)
|
|
label_size++;
|
|
}
|
|
|
|
const double height = (_time_viewport->height()
|
|
- 2 * actualMargin * label_size) * 1.0 / total_rows;
|
|
|
|
if (_device_agent->have_instance() == false){
|
|
assert(false);
|
|
}
|
|
|
|
if (_device_agent->get_work_mode() == LOGIC) {
|
|
GVariant* gvar = _device_agent->get_config(NULL, NULL, SR_CONF_MAX_HEIGHT_VALUE);
|
|
|
|
if (gvar != NULL) {
|
|
max_height = (g_variant_get_byte(gvar) + 1) * MaxHeightUnit;
|
|
g_variant_unref(gvar);
|
|
}
|
|
if (height < 2*actualMargin) {
|
|
actualMargin /= 2;
|
|
_signalHeight = max(1.0, (_time_viewport->height()
|
|
- 2 * actualMargin * label_size) * 1.0 / total_rows);
|
|
}
|
|
else {
|
|
_signalHeight = (height >= max_height) ? max_height : height;
|
|
}
|
|
}
|
|
else if (_device_agent->get_work_mode() == DSO) {
|
|
_signalHeight = (_header->height()
|
|
- horizontalScrollBar()->height()
|
|
- 2 * actualMargin * label_size) * 1.0 / total_rows;
|
|
}
|
|
else {
|
|
_signalHeight = (int)((height <= 0) ? 1 : height);
|
|
}
|
|
|
|
_spanY = _signalHeight + 2 * actualMargin;
|
|
int next_v_offset = actualMargin;
|
|
|
|
for(auto &t : time_traces) {
|
|
t->set_view(this);
|
|
t->set_viewport(_time_viewport);
|
|
if (t->rows_size() == 0)
|
|
continue;
|
|
const double traceHeight = _signalHeight*t->rows_size();
|
|
t->set_totalHeight((int)traceHeight);
|
|
t->set_v_offset(next_v_offset + 0.5 * traceHeight + actualMargin);
|
|
next_v_offset += traceHeight + 2 * actualMargin;
|
|
|
|
view::DsoSignal *dsoSig = NULL;
|
|
if ((dsoSig = dynamic_cast<view::DsoSignal*>(t))) {
|
|
dsoSig->set_scale(dsoSig->get_view_rect().height());
|
|
}
|
|
|
|
view::AnalogSignal *analogSig = NULL;
|
|
if ((analogSig = dynamic_cast<view::AnalogSignal*>(t))) {
|
|
analogSig->set_scale(analogSig->get_totalHeight());
|
|
}
|
|
}
|
|
_time_viewport->clear_measure();
|
|
}
|
|
|
|
header_updated();
|
|
normalize_layout();
|
|
update_scale_offset();
|
|
data_updated();
|
|
}
|
|
|
|
bool View::eventFilter(QObject *object, QEvent *event)
|
|
{
|
|
const QEvent::Type type = event->type();
|
|
if (type == QEvent::MouseMove) {
|
|
const QMouseEvent *const mouse_event = (QMouseEvent*)event;
|
|
if (object == _ruler || object == _time_viewport || object == _fft_viewport) {
|
|
//_hover_point = QPoint(mouse_event->x(), 0);
|
|
double cur_periods = (mouse_event->pos().x() + _offset) * _scale / _ruler->get_min_period();
|
|
int integer_x = round(cur_periods) * _ruler->get_min_period() / _scale - _offset;
|
|
double cur_deviate_x = qAbs(mouse_event->pos().x() - integer_x);
|
|
if (_device_agent->get_work_mode() == LOGIC &&
|
|
cur_deviate_x < 10)
|
|
_hover_point = QPoint(integer_x, mouse_event->pos().y());
|
|
else
|
|
_hover_point = mouse_event->pos();
|
|
} else if (object == _header)
|
|
_hover_point = QPoint(0, mouse_event->y());
|
|
else
|
|
_hover_point = QPoint(-1, -1);
|
|
|
|
hover_point_changed();
|
|
} else if (type == QEvent::Leave) {
|
|
_hover_point = QPoint(-1, -1);
|
|
hover_point_changed();
|
|
}
|
|
|
|
return QObject::eventFilter(object, event);
|
|
}
|
|
|
|
bool View::viewportEvent(QEvent *e)
|
|
{
|
|
switch(e->type()) {
|
|
case QEvent::Paint:
|
|
case QEvent::MouseButtonPress:
|
|
case QEvent::MouseButtonRelease:
|
|
case QEvent::MouseButtonDblClick:
|
|
case QEvent::MouseMove:
|
|
case QEvent::Wheel:
|
|
case QEvent::Gesture:
|
|
return false;
|
|
|
|
default:
|
|
return QAbstractScrollArea::viewportEvent(e);
|
|
}
|
|
}
|
|
|
|
int View::headerWidth()
|
|
{
|
|
int headerWidth = _header->get_nameEditWidth();
|
|
|
|
const auto &traces = get_traces(ALL_VIEW);
|
|
if (!traces.empty()) {
|
|
for(auto &t : traces)
|
|
headerWidth = max(t->get_name_width() + t->get_leftWidth() + t->get_rightWidth(),
|
|
headerWidth);
|
|
}
|
|
|
|
setViewportMargins(headerWidth, RulerHeight, 0, 0);
|
|
|
|
return headerWidth;
|
|
}
|
|
|
|
void View::resizeEvent(QResizeEvent*)
|
|
{
|
|
reconstruct();
|
|
setViewportMargins(headerWidth(), RulerHeight, 0, 0);
|
|
update_margins();
|
|
update_scroll();
|
|
signals_changed();
|
|
if (_device_agent->get_work_mode() == DSO)
|
|
_scale = _session->cur_view_time() / get_view_width();
|
|
|
|
if (_device_agent->get_work_mode() != DSO)
|
|
_maxscale = _session->cur_sampletime() / (get_view_width() * MaxViewRate);
|
|
else
|
|
_maxscale = 1e9;
|
|
|
|
_scale = min(_scale, _maxscale);
|
|
|
|
_ruler->update();
|
|
_header->header_resize();
|
|
set_update(_time_viewport, true);
|
|
set_update(_fft_viewport, true);
|
|
resize();
|
|
}
|
|
|
|
void View::h_scroll_value_changed(int value)
|
|
{
|
|
if (_updating_scroll)
|
|
return;
|
|
|
|
_preOffset = _offset;
|
|
|
|
const int range = horizontalScrollBar()->maximum();
|
|
if (range < MaxScrollValue)
|
|
_offset = value;
|
|
else {
|
|
int64_t length = 0;
|
|
int64_t offset = 0;
|
|
get_scroll_layout(length, offset);
|
|
_offset = floor(value * 1.0 / MaxScrollValue * length);
|
|
}
|
|
|
|
_offset = max(min(_offset, get_max_offset()), get_min_offset());
|
|
|
|
if (_offset != _preOffset) {
|
|
_ruler->update();
|
|
viewport_update();
|
|
}
|
|
}
|
|
|
|
void View::v_scroll_value_changed(int value)
|
|
{
|
|
(void)value;
|
|
_header->update();
|
|
viewport_update();
|
|
}
|
|
|
|
void View::data_updated()
|
|
{
|
|
setViewportMargins(headerWidth(), RulerHeight, 0, 0);
|
|
update_margins();
|
|
|
|
// Update the scroll bars
|
|
update_scroll();
|
|
|
|
// update scale & offset
|
|
update_scale_offset();
|
|
|
|
// Repaint the view
|
|
_time_viewport->unshow_wait_trigger();
|
|
set_update(_time_viewport, true);
|
|
set_update(_fft_viewport, true);
|
|
viewport_update();
|
|
_ruler->update();
|
|
}
|
|
|
|
void View::update_margins()
|
|
{
|
|
_ruler->setGeometry(_viewcenter->x(), 0, get_view_width(), _viewcenter->y());
|
|
|
|
_header->setGeometry(0, _viewcenter->y(), _viewcenter->x(), _viewcenter->height());
|
|
|
|
_devmode->setGeometry(0, 0, _viewcenter->x(), _viewcenter->y());
|
|
}
|
|
|
|
void View::header_updated()
|
|
{
|
|
headerWidth();
|
|
update_margins();
|
|
|
|
// Update the scroll bars
|
|
update_scroll();
|
|
|
|
viewport_update();
|
|
_header->update();
|
|
}
|
|
|
|
void View::marker_time_changed()
|
|
{
|
|
_ruler->update();
|
|
viewport_update();
|
|
}
|
|
|
|
void View::on_traces_moved()
|
|
{
|
|
update_scroll();
|
|
set_update(_time_viewport, true);
|
|
viewport_update();
|
|
}
|
|
|
|
/*
|
|
* cursorList
|
|
*/
|
|
std::list<Cursor*>& View::get_cursorList()
|
|
{
|
|
return _cursorList;
|
|
}
|
|
|
|
Cursor* View::get_trig_cursor()
|
|
{
|
|
return _trig_cursor;
|
|
}
|
|
|
|
Cursor* View::get_search_cursor()
|
|
{
|
|
return _search_cursor;
|
|
}
|
|
|
|
Ruler* View::get_ruler()
|
|
{
|
|
return _ruler;
|
|
}
|
|
|
|
void View::add_cursor(QColor color, uint64_t index)
|
|
{
|
|
Cursor *newCursor = new Cursor(*this, color, index);
|
|
_cursorList.push_back(newCursor);
|
|
cursor_update();
|
|
}
|
|
|
|
void View::del_cursor(Cursor* cursor)
|
|
{
|
|
assert(cursor);
|
|
|
|
_cursorList.remove(cursor);
|
|
delete cursor;
|
|
cursor_update();
|
|
}
|
|
|
|
void View::clear_cursors()
|
|
{
|
|
for (auto c : _cursorList){
|
|
delete c;
|
|
}
|
|
_cursorList.clear();
|
|
}
|
|
|
|
void View::set_cursor_middle(int index)
|
|
{
|
|
assert(index < (int)_cursorList.size());
|
|
|
|
auto i = _cursorList.begin();
|
|
while (index-- != 0)
|
|
i++;
|
|
set_scale_offset(_scale, (*i)->index() / (_session->cur_snap_samplerate() * _scale) - (get_view_width() / 2));
|
|
}
|
|
|
|
void View::on_measure_updated()
|
|
{
|
|
_active_viewport = dynamic_cast<Viewport *>(sender());
|
|
measure_updated();
|
|
}
|
|
|
|
QString View::get_measure(QString option)
|
|
{
|
|
if (_active_viewport) {
|
|
return _active_viewport->get_measure(option);
|
|
}
|
|
return Unknown_Str;
|
|
}
|
|
|
|
QString View::get_cm_time(int index)
|
|
{
|
|
return _ruler->format_real_time(get_cursor_samples(index), _session->cur_snap_samplerate());
|
|
}
|
|
|
|
QString View::get_cm_delta(int index1, int index2)
|
|
{
|
|
if (index1 == index2)
|
|
return "0";
|
|
|
|
uint64_t samples1 = get_cursor_samples(index1);
|
|
uint64_t samples2 = get_cursor_samples(index2);
|
|
uint64_t delta_sample = (samples1 > samples2) ? samples1 - samples2 : samples2 - samples1;
|
|
return _ruler->format_real_time(delta_sample, _session->cur_snap_samplerate());
|
|
}
|
|
|
|
QString View::get_index_delta(uint64_t start, uint64_t end)
|
|
{
|
|
if (start == end)
|
|
return "0";
|
|
|
|
uint64_t delta_sample = (start > end) ? start - end : end - start;
|
|
return _ruler->format_real_time(delta_sample, _session->cur_snap_samplerate());
|
|
}
|
|
|
|
uint64_t View::get_cursor_samples(int index)
|
|
{
|
|
assert(index < (int)_cursorList.size());
|
|
|
|
uint64_t ret = 0;
|
|
int curIndex = 0;
|
|
for (list<Cursor*>::iterator i = _cursorList.begin();
|
|
i != _cursorList.end(); i++) {
|
|
if (index == curIndex) {
|
|
ret = (*i)->index();
|
|
}
|
|
curIndex++;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
void View::set_measure_en(int enable)
|
|
{
|
|
_time_viewport->set_measure_en(enable);
|
|
_fft_viewport->set_measure_en(enable);
|
|
}
|
|
|
|
void View::on_state_changed(bool stop)
|
|
{
|
|
if (stop) {
|
|
_time_viewport->stop_trigger_timer();
|
|
_fft_viewport->stop_trigger_timer();
|
|
}
|
|
update_scale_offset();
|
|
}
|
|
|
|
QRect View::get_view_rect()
|
|
{
|
|
if (_device_agent->get_work_mode() == DSO) {
|
|
const auto &sigs = _session->get_signals();
|
|
for(auto &s : sigs) {
|
|
return s->get_view_rect();
|
|
}
|
|
}
|
|
|
|
return _viewcenter->rect();
|
|
}
|
|
|
|
int View::get_view_width()
|
|
{
|
|
int view_width = 0;
|
|
if (_device_agent->get_work_mode() == DSO) {
|
|
const auto &sigs = _session->get_signals();
|
|
for(auto &s : sigs) {
|
|
view_width = max(view_width, s->get_view_rect().width());
|
|
}
|
|
} else {
|
|
view_width = _viewcenter->width();
|
|
}
|
|
|
|
return view_width;
|
|
}
|
|
|
|
int View::get_view_height()
|
|
{
|
|
int view_height = 0;
|
|
if (_device_agent->get_work_mode() == DSO) {
|
|
const auto &sigs = _session->get_signals();
|
|
for(auto &s : sigs) {
|
|
view_height = max(view_height, s->get_view_rect().height());
|
|
}
|
|
} else {
|
|
view_height = _viewcenter->height();
|
|
}
|
|
|
|
return view_height;
|
|
}
|
|
|
|
int64_t View::get_min_offset()
|
|
{
|
|
if (MaxViewRate > 1)
|
|
return floor(get_view_width() * (1 - MaxViewRate));
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
int64_t View::get_max_offset()
|
|
{
|
|
return ceil((_session->cur_snap_sampletime() / _scale) -
|
|
(get_view_width() * MaxViewRate));
|
|
}
|
|
|
|
// -- calibration dialog
|
|
void View::show_calibration()
|
|
{
|
|
_cali->update_device_info();
|
|
_cali->show();
|
|
}
|
|
|
|
void View::hide_calibration()
|
|
{
|
|
_cali->hide();
|
|
}
|
|
|
|
void View::vDial_updated()
|
|
{
|
|
if (_cali->isVisible()) {
|
|
_cali->update_device_info();
|
|
}
|
|
auto math_trace = _session->get_math_trace();
|
|
if (math_trace && math_trace->enabled()) {
|
|
math_trace->update_vDial();
|
|
}
|
|
}
|
|
|
|
// -- lissajous figure
|
|
void View::show_lissajous(bool show)
|
|
{
|
|
_show_lissajous = show;
|
|
signals_changed();
|
|
}
|
|
|
|
void View::show_region(uint64_t start, uint64_t end, bool keep)
|
|
{
|
|
assert(start <= end);
|
|
if (keep) {
|
|
set_all_update(true);
|
|
update();
|
|
} else if (_session->get_map_zoom() == 0) {
|
|
const double ideal_scale = (end-start) * 2.0 / _session->cur_snap_samplerate() / get_view_width();
|
|
const double new_scale = max (min(ideal_scale, _maxscale), _minscale);
|
|
const double new_off = (start + end) * 0.5 / (_session->cur_snap_samplerate() * new_scale) - (get_view_width() / 2);
|
|
set_scale_offset(new_scale, new_off);
|
|
} else {
|
|
const double new_scale = scale();
|
|
const double new_off = (start + end) * 0.5 / (_session->cur_snap_samplerate() * new_scale) - (get_view_width() / 2);
|
|
set_scale_offset(new_scale, new_off);
|
|
}
|
|
}
|
|
|
|
void View::viewport_update()
|
|
{
|
|
_viewcenter->update();
|
|
for(QWidget *viewport : _viewport_list)
|
|
viewport->update();
|
|
}
|
|
|
|
void View::splitterMoved(int pos, int index)
|
|
{
|
|
(void)pos;
|
|
(void)index;
|
|
signals_changed();
|
|
}
|
|
|
|
void View::reload()
|
|
{
|
|
clear();
|
|
|
|
/*
|
|
* if headerwidth not change, viewport height will not be updated
|
|
* lead to a wrong signal height
|
|
*/
|
|
reconstruct();
|
|
}
|
|
|
|
void View::clear()
|
|
{
|
|
show_trig_cursor(false);
|
|
|
|
if (_device_agent->get_work_mode() != DSO) {
|
|
show_xcursors(false);
|
|
} else {
|
|
if (!get_xcursorList().empty())
|
|
show_xcursors(true);
|
|
}
|
|
}
|
|
|
|
void View::reconstruct()
|
|
{
|
|
if (_device_agent->get_work_mode() == DSO)
|
|
_viewbottom->setFixedHeight(DsoStatusHeight);
|
|
else
|
|
_viewbottom->setFixedHeight(StatusHeight);
|
|
_viewbottom->reload();
|
|
}
|
|
|
|
void View::repeat_show()
|
|
{
|
|
_viewbottom->update();
|
|
}
|
|
|
|
void View::set_capture_status()
|
|
{
|
|
bool triggered;
|
|
int progress;
|
|
if (_session->get_capture_status(triggered, progress)) {
|
|
_viewbottom->set_capture_status(triggered, progress);
|
|
_viewbottom->update();
|
|
}
|
|
}
|
|
|
|
bool View::get_dso_trig_moved()
|
|
{
|
|
return _time_viewport->get_dso_trig_moved();
|
|
}
|
|
|
|
/*
|
|
* horizental cursors
|
|
*/
|
|
bool View::xcursors_shown()
|
|
{
|
|
return _show_xcursors;
|
|
}
|
|
|
|
void View::show_xcursors(bool show)
|
|
{
|
|
_show_xcursors = show;
|
|
}
|
|
|
|
std::list<XCursor*>& View::get_xcursorList()
|
|
{
|
|
return _xcursorList;
|
|
}
|
|
|
|
void View::add_xcursor(QColor color, double value0, double value1)
|
|
{
|
|
XCursor *newXCursor = new XCursor(*this, color, value0, value1);
|
|
_xcursorList.push_back(newXCursor);
|
|
xcursor_update();
|
|
}
|
|
|
|
void View::del_xcursor(XCursor* xcursor)
|
|
{
|
|
assert(xcursor);
|
|
|
|
_xcursorList.remove(xcursor);
|
|
delete xcursor;
|
|
xcursor_update();
|
|
}
|
|
|
|
ViewStatus* View::get_viewstatus()
|
|
{
|
|
return _viewbottom;
|
|
}
|
|
|
|
bool View::back_ready()
|
|
{
|
|
return _back_ready;
|
|
}
|
|
|
|
void View::set_back(bool ready)
|
|
{
|
|
_back_ready = ready;
|
|
}
|
|
|
|
double View::index2pixel(uint64_t index, bool has_hoff)
|
|
{
|
|
const double samples_per_pixel = session().cur_snap_samplerate() * scale();
|
|
double pixels;
|
|
if (has_hoff)
|
|
pixels = index/samples_per_pixel - offset() + trig_hoff()/samples_per_pixel;
|
|
else
|
|
pixels = index/samples_per_pixel - offset();
|
|
|
|
return pixels;
|
|
}
|
|
|
|
uint64_t View::pixel2index(double pixel)
|
|
{
|
|
const double samples_per_pixel = session().cur_snap_samplerate() * scale();
|
|
uint64_t index = (pixel + offset()) * samples_per_pixel - trig_hoff();
|
|
|
|
return index;
|
|
}
|
|
|
|
void View::set_receive_len(uint64_t len)
|
|
{
|
|
if (_time_viewport)
|
|
_time_viewport->set_receive_len(len);
|
|
|
|
if (_fft_viewport)
|
|
_fft_viewport->set_receive_len(len);
|
|
}
|
|
|
|
int View::get_cursor_index_by_key(uint64_t key)
|
|
{
|
|
int dex = 0;
|
|
for (auto c : _cursorList){
|
|
if (c->get_key() == key){
|
|
return dex;
|
|
}
|
|
++dex;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
void View::check_calibration()
|
|
{
|
|
if (_device_agent->get_work_mode() == DSO){
|
|
GVariant* gvar = _device_agent->get_config(NULL, NULL, SR_CONF_CALI);
|
|
if (gvar != NULL) {
|
|
bool cali = g_variant_get_boolean(gvar);
|
|
g_variant_unref(gvar);
|
|
if (cali) {
|
|
show_calibration();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void View::OnMessage(int msg)
|
|
{
|
|
switch (msg)
|
|
{
|
|
case DSV_MSG_DEVICE_OPTIONS_UPDATED:
|
|
check_calibration();
|
|
break;
|
|
case DSV_MSG_COLLECT_START_PREV:
|
|
capture_init();
|
|
break;
|
|
case DSV_MSG_DEVICE_DURATION_UPDATED:
|
|
timebase_changed();
|
|
break;
|
|
case DSV_MSG_DEVICE_MODE_CHANGED:
|
|
mode_changed();
|
|
break;
|
|
case DSV_MSG_CURRENT_DEVICE_CHANGED:
|
|
reload();
|
|
break;
|
|
}
|
|
}
|
|
|
|
} // namespace view
|
|
} // namespace pv
|