From c02e2d1e9e43d1aa104e258e4204e17617896f3e Mon Sep 17 00:00:00 2001 From: Anatol Pomozov Date: Mon, 14 Dec 2015 11:26:14 -0800 Subject: [PATCH 01/32] Fix misspellings using codespell tool --- DSView/pv/data/decoderstack.cpp | 2 +- DSView/pv/data/decoderstack.h | 2 +- NEWS | 8 ++++---- libsigrok4DSL/hardware/demo/demo.c | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/DSView/pv/data/decoderstack.cpp b/DSView/pv/data/decoderstack.cpp index 1df0abb0..6d9442bf 100644 --- a/DSView/pv/data/decoderstack.cpp +++ b/DSView/pv/data/decoderstack.cpp @@ -409,7 +409,7 @@ void DecoderStack::decode_proc() prev_di = di; } - // Get the intial sample count + // Get the initial sample count { unique_lock input_lock(_input_mutex); sample_count = _sample_count = _snapshot->get_sample_count(); diff --git a/DSView/pv/data/decoderstack.h b/DSView/pv/data/decoderstack.h index b2cd65f8..24713e05 100644 --- a/DSView/pv/data/decoderstack.h +++ b/DSView/pv/data/decoderstack.h @@ -148,7 +148,7 @@ private: pv::SigSession &_session; /** - * This mutex prevents more than one decode operation occuring + * This mutex prevents more than one decode operation occurring * concurrently. * @todo A proper solution should be implemented to allow multiple * decode operations. diff --git a/NEWS b/NEWS index 7dd920f8..d13ac34f 100644 --- a/NEWS +++ b/NEWS @@ -50,14 +50,14 @@ * Improve measure function @ LA mode * Add duty cycle measure @ LA mode * Fix out of range issue @ LA mode - * Add export funtion, support csv/vcd/gnuplot/zip @ LA mode; and csv @ DSO mode + * Add export function, support csv/vcd/gnuplot/zip @ LA mode; and csv @ DSO mode * Add x1/x10/x100 probe options @ DSO mode * Add measure function @ DSO mode * Add voltage display of trigger value @ DSO mode * Fix wave disappear issue @ DSO mode * Fix trigger issue @ DSO stream mode * Fix data repeat when set trigger @ LA stream mode - * Keep channel settings when reload occured @ LA mode + * Keep channel settings when reload occurred @ LA mode * Fix decoder issue when capture part of data @ LA mode * Fix stack decoder add issue @ LA mode * Fix other bugs @@ -84,14 +84,14 @@ * Improve measure function @ LA mode * Add duty cycle measure @ LA mode * Fix out of range issue @ LA mode - * Add export funtion, support csv/vcd/gnuplot/zip @ LA mode; and csv @ DSO mode + * Add export function, support csv/vcd/gnuplot/zip @ LA mode; and csv @ DSO mode * Add x1/x10/x100 probe options @ DSO mode * Add measure function @ DSO mode * Add voltage display of trigger value @ DSO mode * Fix wave disappear issue @ DSO mode * Fix trigger issue @ DSO stream mode * Fix data repeat when set trigger @ LA stream mode - * Keep channel settings when reload occured @ LA mode + * Keep channel settings when reload occurred @ LA mode * Fix decoder issue when capture part of data @ LA mode * Fix stack decoder add issue @ LA mode * Fix other bugsnalyzer mode diff --git a/libsigrok4DSL/hardware/demo/demo.c b/libsigrok4DSL/hardware/demo/demo.c index a31467a5..03a0f0ce 100644 --- a/libsigrok4DSL/hardware/demo/demo.c +++ b/libsigrok4DSL/hardware/demo/demo.c @@ -1052,7 +1052,7 @@ static int hw_dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data) (void)cb_data; - sr_dbg("Stopping aquisition."); + sr_dbg("Stopping acquisition."); devc->stop = TRUE; sr_session_source_remove_channel(devc->channel); From a723b459d785b5a71af517b6c61df9e1320d36a4 Mon Sep 17 00:00:00 2001 From: teyssieuman Date: Wed, 6 Jan 2016 21:26:58 +0100 Subject: [PATCH 02/32] Remove some compilation warnings on linux --- DSView/pv/data/dsosnapshot.cpp | 6 ++---- DSView/pv/data/dsosnapshot.h | 4 ++-- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/DSView/pv/data/dsosnapshot.cpp b/DSView/pv/data/dsosnapshot.cpp index 4e0e1b26..cf245049 100644 --- a/DSView/pv/data/dsosnapshot.cpp +++ b/DSView/pv/data/dsosnapshot.cpp @@ -237,9 +237,8 @@ void DsoSnapshot::append_payload_to_envelope_levels(bool header) _envelope_done = true; } -double DsoSnapshot::cal_vrms(double zero_off, int index) const +double DsoSnapshot::cal_vrms(double zero_off, unsigned int index) const { - assert(index >= 0); assert(index < _channel_num); // root-meam-squart value @@ -272,9 +271,8 @@ double DsoSnapshot::cal_vrms(double zero_off, int index) const return vrms; } -double DsoSnapshot::cal_vmean(int index) const +double DsoSnapshot::cal_vmean(unsigned int index) const { - assert(index >= 0); assert(index < _channel_num); // mean value diff --git a/DSView/pv/data/dsosnapshot.h b/DSView/pv/data/dsosnapshot.h index eb3788bc..56dfb18d 100644 --- a/DSView/pv/data/dsosnapshot.h +++ b/DSView/pv/data/dsosnapshot.h @@ -84,8 +84,8 @@ public: void enable_envelope(bool enable); - double cal_vrms(double zero_off, int index) const; - double cal_vmean(int index) const; + double cal_vrms(double zero_off, unsigned int index) const; + double cal_vmean(unsigned int index) const; private: void reallocate_envelope(Envelope &l); From 73cbab6f43d21a6868fccf1270a5d5d1fc1e3180 Mon Sep 17 00:00:00 2001 From: teyssieuman Date: Wed, 6 Jan 2016 22:07:03 +0100 Subject: [PATCH 03/32] Supress the "QT" error message that appears here: https://www.youtube.com/watch?v=bjITW4hfILs --- DSView/pv/dock/protocoldock.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DSView/pv/dock/protocoldock.cpp b/DSView/pv/dock/protocoldock.cpp index 37463923..48280abf 100644 --- a/DSView/pv/dock/protocoldock.cpp +++ b/DSView/pv/dock/protocoldock.cpp @@ -105,7 +105,7 @@ void ProtocolDock::paintEvent(QPaintEvent *) { QStyleOption opt; opt.init(this); - QPainter p(this); + QPainter p(this->viewport()); style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); } From 01569e928c59d91f07428e11b63a3fee48763dfe Mon Sep 17 00:00:00 2001 From: Bryant Date: Fri, 8 Jan 2016 19:54:51 -0800 Subject: [PATCH 04/32] Add a desktop file. Uncertain if this also needs an installation script change or if this is good enough. --- dsview.desktop | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 dsview.desktop diff --git a/dsview.desktop b/dsview.desktop new file mode 100644 index 00000000..cbc24d00 --- /dev/null +++ b/dsview.desktop @@ -0,0 +1,9 @@ +[Desktop Entry] +Exec=DSView +Name=DSView +GenericName=Logic Analyzer +Comment=DreamStudio Logic Analyzer +Icon=dsview +Type=Application +Terminal=false +Categories=Development From dd7b0106bab12865aaa45122cf76b76a81eba8a2 Mon Sep 17 00:00:00 2001 From: Bryant Date: Fri, 8 Jan 2016 19:49:17 -0800 Subject: [PATCH 05/32] Ignore compilation intermediates. --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index caa12f24..2b0f916e 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ *.lo CMakeFiles CMakeCache.txt +*.cxx_parameters autom4te.cache *.cmake !cmake_modules From f0aff5e7ce63594f5714768836cc8b6e9c57fe5c Mon Sep 17 00:00:00 2001 From: DreamSourceLab Date: Fri, 25 Mar 2016 14:34:06 +0800 Subject: [PATCH 06/32] Fix memory release issue when added protocol decoder --- DSView/pv/data/decoderstack.cpp | 2 ++ DSView/pv/dock/protocoldock.cpp | 8 ++++---- DSView/pv/sigsession.cpp | 15 ++++++++------- 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/DSView/pv/data/decoderstack.cpp b/DSView/pv/data/decoderstack.cpp index 1df0abb0..056bdfe7 100644 --- a/DSView/pv/data/decoderstack.cpp +++ b/DSView/pv/data/decoderstack.cpp @@ -213,6 +213,8 @@ void DecoderStack::clear() void DecoderStack::stop_decode() { + _snapshot.reset(); + if(_decode_state == Stopped) return; diff --git a/DSView/pv/dock/protocoldock.cpp b/DSView/pv/dock/protocoldock.cpp index 37463923..7756af9e 100644 --- a/DSView/pv/dock/protocoldock.cpp +++ b/DSView/pv/dock/protocoldock.cpp @@ -103,10 +103,10 @@ int ProtocolDock::decoder_name_cmp(const void *a, const void *b) void ProtocolDock::paintEvent(QPaintEvent *) { - QStyleOption opt; - opt.init(this); - QPainter p(this); - style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); + //QStyleOption opt; + //opt.init(this); + //QPainter p(this); + //style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); } void ProtocolDock::add_protocol() diff --git a/DSView/pv/sigsession.cpp b/DSView/pv/sigsession.cpp index fb547f52..b4dfa217 100644 --- a/DSView/pv/sigsession.cpp +++ b/DSView/pv/sigsession.cpp @@ -608,6 +608,13 @@ void SigSession::read_sample_rate(const sr_dev_inst *const sdi) void SigSession::feed_in_header(const sr_dev_inst *sdi) { +#ifdef ENABLE_DECODE + for (vector< boost::shared_ptr >::iterator i = + _decode_traces.begin(); + i != _decode_traces.end(); + i++) + (*i)->decoder()->stop_decode(); +#endif read_sample_rate(sdi); //receive_data(0); } @@ -1098,13 +1105,7 @@ void SigSession::data_feed_in(const struct sr_dev_inst *sdi, _cur_dso_snapshot.reset(); _cur_analog_snapshot.reset(); } -#ifdef ENABLE_DECODE - for (vector< boost::shared_ptr >::iterator i = - _decode_traces.begin(); - i != _decode_traces.end(); - i++) - (*i)->decoder()->stop_decode(); -#endif + frame_ended(); break; } From a5ead17590476fb284d17f3c646dd3bbbcbaa1bd Mon Sep 17 00:00:00 2001 From: DreamSourceLab Date: Sun, 27 Mar 2016 12:03:20 +0800 Subject: [PATCH 07/32] Add region decode feature --- DSView/pv/data/decode/decoder.cpp | 21 + DSView/pv/data/decode/decoder.h | 7 + DSView/pv/data/decoderstack.cpp | 92 +++- DSView/pv/data/decoderstack.h | 5 +- DSView/pv/dock/measuredock.cpp | 4 +- DSView/pv/dock/measuredock.h | 2 +- DSView/pv/sigsession.cpp | 10 + DSView/pv/view/analogsignal.cpp | 6 +- DSView/pv/view/decodetrace.cpp | 209 ++++++-- DSView/pv/view/decodetrace.h | 14 + DSView/pv/view/groupsignal.cpp | 6 +- DSView/pv/view/logicsignal.cpp | 8 +- DSView/pv/view/trace.cpp | 19 +- DSView/pv/view/trace.h | 7 +- DSView/pv/view/view.cpp | 2 +- DSView/pv/view/viewport.cpp | 845 +++++++++++++++++------------- DSView/pv/view/viewport.h | 38 +- 17 files changed, 818 insertions(+), 477 deletions(-) diff --git a/DSView/pv/data/decode/decoder.cpp b/DSView/pv/data/decode/decoder.cpp index 00d6a1f5..5c27c085 100644 --- a/DSView/pv/data/decode/decoder.cpp +++ b/DSView/pv/data/decode/decoder.cpp @@ -93,11 +93,32 @@ void Decoder::set_option(const char *id, GVariant *value) _setted = true; } +void Decoder::set_decode_region(uint64_t start, uint64_t end) +{ + _decode_start_back = start; + _decode_end_back = end; + if (_decode_start != start || + _decode_end != end) + _setted = true; +} + +uint64_t Decoder::decode_start() const +{ + return _decode_start; +} + +uint64_t Decoder::decode_end() const +{ + return _decode_end; +} + bool Decoder::commit() { if (_setted) { _probes = _probes_back; _options = _options_back; + _decode_start = _decode_start_back; + _decode_end = _decode_end_back; _setted = false; return true; } else { diff --git a/DSView/pv/data/decode/decoder.h b/DSView/pv/data/decode/decoder.h index ae51c1d3..607bc0b8 100644 --- a/DSView/pv/data/decode/decoder.h +++ b/DSView/pv/data/decode/decoder.h @@ -73,6 +73,10 @@ public: std::set< boost::shared_ptr > get_data(); + void set_decode_region(uint64_t start, uint64_t end); + uint64_t decode_start() const; + uint64_t decode_end() const; + bool commit(); private: @@ -88,6 +92,9 @@ private: _probes_back; std::map _options_back; + uint64_t _decode_start, _decode_end; + uint64_t _decode_start_back, _decode_end_back; + bool _setted; }; diff --git a/DSView/pv/data/decoderstack.cpp b/DSView/pv/data/decoderstack.cpp index 056bdfe7..ed720090 100644 --- a/DSView/pv/data/decoderstack.cpp +++ b/DSView/pv/data/decoderstack.cpp @@ -333,43 +333,83 @@ boost::optional DecoderStack::wait_for_data() const _sample_count); } +//void DecoderStack::decode_data( +// const uint64_t sample_count, const unsigned int unit_size, +// srd_session *const session) +//{ +// //uint8_t chunk[DecodeChunkLength]; +// uint8_t *chunk = NULL; +// //chunk = (uint8_t *)realloc(chunk, DecodeChunkLength); + +// const uint64_t chunk_sample_count = +// DecodeChunkLength / _snapshot->unit_size(); + +// for (uint64_t i = 0; +// !boost::this_thread::interruption_requested() && +// i < sample_count; +// i += chunk_sample_count) +// { +// //lock_guard decode_lock(_global_decode_mutex); + +// const uint64_t chunk_end = min( +// i + chunk_sample_count, sample_count); +// chunk = _snapshot->get_samples(i, chunk_end); + +// if (srd_session_send(session, i, i + sample_count, chunk, +// (chunk_end - i) * unit_size, unit_size) != SRD_OK) { +// _error_message = tr("Decoder reported an error"); +// break; +// } + +// { +// lock_guard lock(_output_mutex); +// _samples_decoded = chunk_end; +// } + +// if (i % DecodeNotifyPeriod == 0) +// new_decode_data(); + +// } +// _options_changed = false; +// decode_done(); +// //new_decode_data(); +//} + void DecoderStack::decode_data( - const uint64_t sample_count, const unsigned int unit_size, - srd_session *const session) + const uint64_t decode_start, const uint64_t decode_end, + const unsigned int unit_size, srd_session *const session) { - //uint8_t chunk[DecodeChunkLength]; uint8_t *chunk = NULL; - //chunk = (uint8_t *)realloc(chunk, DecodeChunkLength); const uint64_t chunk_sample_count = - DecodeChunkLength / _snapshot->unit_size(); + DecodeChunkLength / _snapshot->unit_size(); - for (uint64_t i = 0; - !boost::this_thread::interruption_requested() && - i < sample_count; - i += chunk_sample_count) - { + for (uint64_t i = decode_start; + !boost::this_thread::interruption_requested() && + i < decode_end; + i += chunk_sample_count) + { //lock_guard decode_lock(_global_decode_mutex); const uint64_t chunk_end = min( - i + chunk_sample_count, sample_count); + i + chunk_sample_count, decode_end); chunk = _snapshot->get_samples(i, chunk_end); - if (srd_session_send(session, i, i + sample_count, chunk, + if (srd_session_send(session, i, chunk_end, chunk, (chunk_end - i) * unit_size, unit_size) != SRD_OK) { - _error_message = tr("Decoder reported an error"); - break; - } + _error_message = tr("Decoder reported an error"); + break; + } - { - lock_guard lock(_output_mutex); - _samples_decoded = chunk_end; - } + { + lock_guard lock(_output_mutex); + _samples_decoded = chunk_end - decode_start + 1; + } if (i % DecodeNotifyPeriod == 0) new_decode_data(); - } + } _options_changed = false; decode_done(); //new_decode_data(); @@ -382,6 +422,8 @@ void DecoderStack::decode_proc() optional sample_count; srd_session *session; srd_decoder_inst *prev_di = NULL; + uint64_t decode_start; + uint64_t decode_end; assert(_snapshot); @@ -409,6 +451,8 @@ void DecoderStack::decode_proc() srd_inst_stack (session, prev_di, di); prev_di = di; + decode_start = dec->decode_start(); + decode_end = min(dec->decode_end(), _snapshot->get_sample_count()); } // Get the intial sample count @@ -429,7 +473,8 @@ void DecoderStack::decode_proc() // do { // decode_data(*sample_count, unit_size, session); // } while(_error_message.isEmpty() && (sample_count = wait_for_data())); - decode_data(*sample_count, unit_size, session); + //decode_data(*sample_count, unit_size, session); + decode_data(decode_start, decode_end, unit_size, session); // Destroy the session srd_session_destroy(session); @@ -439,7 +484,10 @@ void DecoderStack::decode_proc() uint64_t DecoderStack::sample_count() const { - return _sample_count; + if (_snapshot) + return _snapshot->get_sample_count(); + else + return 0; } void DecoderStack::annotation_callback(srd_proto_data *pdata, void *decoder) diff --git a/DSView/pv/data/decoderstack.h b/DSView/pv/data/decoderstack.h index b2cd65f8..0e71dd52 100644 --- a/DSView/pv/data/decoderstack.h +++ b/DSView/pv/data/decoderstack.h @@ -125,7 +125,10 @@ public: private: boost::optional wait_for_data() const; - void decode_data(const uint64_t sample_count, +// void decode_data(const uint64_t sample_count, +// const unsigned int unit_size, srd_session *const session); + + void decode_data(const uint64_t decode_start, const uint64_t decode_end, const unsigned int unit_size, srd_session *const session); void decode_proc(); diff --git a/DSView/pv/dock/measuredock.cpp b/DSView/pv/dock/measuredock.cpp index 03cd4e1f..6a329021 100644 --- a/DSView/pv/dock/measuredock.cpp +++ b/DSView/pv/dock/measuredock.cpp @@ -134,7 +134,7 @@ MeasureDock::MeasureDock(QWidget *parent, View &view, SigSession &session) : connect(_t3_comboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(delta_update())); connect(_fen_checkBox, SIGNAL(stateChanged(int)), &_view, SLOT(set_measure_en(int))); - connect(_view.get_viewport(), SIGNAL(mouse_measure()), this, SLOT(mouse_measure())); + connect(_view.get_viewport(), SIGNAL(measure_updated()), this, SLOT(measure_updated())); this->setWidget(_widget); _widget->setGeometry(0, 0, sizeHint().width(), 2000); @@ -220,7 +220,7 @@ void MeasureDock::cursor_update() update(); } -void MeasureDock::mouse_measure() +void MeasureDock::measure_updated() { _width_label->setText(_view.get_viewport()->get_measure("width")); _period_label->setText(_view.get_viewport()->get_measure("period")); diff --git a/DSView/pv/dock/measuredock.h b/DSView/pv/dock/measuredock.h index 6b13ec7d..8cf455fa 100644 --- a/DSView/pv/dock/measuredock.h +++ b/DSView/pv/dock/measuredock.h @@ -74,7 +74,7 @@ private slots: public slots: void cursor_update(); void cursor_moved(); - void mouse_measure(); + void measure_updated(); private: SigSession &_session; diff --git a/DSView/pv/sigsession.cpp b/DSView/pv/sigsession.cpp index b4dfa217..19bf99f5 100644 --- a/DSView/pv/sigsession.cpp +++ b/DSView/pv/sigsession.cpp @@ -1104,6 +1104,9 @@ void SigSession::data_feed_in(const struct sr_dev_inst *sdi, _cur_logic_snapshot.reset(); _cur_dso_snapshot.reset(); _cur_analog_snapshot.reset(); + + BOOST_FOREACH(const boost::shared_ptr d, _decode_traces) + d->frame_ended(); } frame_ended(); @@ -1287,6 +1290,13 @@ bool SigSession::add_decoder(srd_decoder *const dec) boost::shared_ptr d( new view::DecodeTrace(*this, decoder_stack, _decode_traces.size())); + // set view early for decode start/end region setting + BOOST_FOREACH(const boost::shared_ptr s, _signals) { + if (s->get_view()) { + d->set_view(s->get_view()); + break; + } + } if (d->create_popup()) { _decode_traces.push_back(d); ret = true; diff --git a/DSView/pv/view/analogsignal.cpp b/DSView/pv/view/analogsignal.cpp index dbef9c00..595dff32 100644 --- a/DSView/pv/view/analogsignal.cpp +++ b/DSView/pv/view/analogsignal.cpp @@ -58,7 +58,7 @@ AnalogSignal::AnalogSignal(boost::shared_ptr dev_inst, _data(data) { _colour = SignalColours[probe->index % countof(SignalColours)]; - _scale = _signalHeight * 1.0f / 65536; + _scale = _totalHeight * 1.0f / 65536; } AnalogSignal::~AnalogSignal() @@ -81,7 +81,7 @@ void AnalogSignal::paint_mid(QPainter &p, int left, int right) assert(_view); assert(right >= left); - const int y = get_y() + _signalHeight * 0.5; + const int y = get_y() + _totalHeight * 0.5; const double scale = _view->scale(); assert(scale > 0); const double offset = _view->offset(); @@ -91,7 +91,7 @@ void AnalogSignal::paint_mid(QPainter &p, int left, int right) if (snapshots.empty()) return; - _scale = _signalHeight * 1.0f / 65536; + _scale = _totalHeight * 1.0f / 65536; const boost::shared_ptr &snapshot = snapshots.front(); diff --git a/DSView/pv/view/decodetrace.cpp b/DSView/pv/view/decodetrace.cpp index d08627ea..a8c84244 100644 --- a/DSView/pv/view/decodetrace.cpp +++ b/DSView/pv/view/decodetrace.cpp @@ -50,14 +50,10 @@ extern "C" { #include "../widgets/decodergroupbox.h" #include "../widgets/decodermenu.h" #include "../device/devinst.h" +#include "../view/cursor.h" -using boost::dynamic_pointer_cast; -using boost::shared_ptr; -using std::list; -using std::max; -using std::map; -using std::min; -using std::vector; +using namespace boost; +using namespace std; namespace pv { namespace view { @@ -114,12 +110,21 @@ const QColor DecodeTrace::OutlineColours[16] = { QColor(0x6B, 0x23, 0x37) }; +const QString DecodeTrace::RegionStart = "Start"; +const QString DecodeTrace::RegionEnd = "End "; + DecodeTrace::DecodeTrace(pv::SigSession &session, boost::shared_ptr decoder_stack, int index) : Trace(QString::fromUtf8( decoder_stack->stack().front()->decoder()->name), index, SR_CHANNEL_DECODER), _session(session), _decoder_stack(decoder_stack), + _decode_start(0), + _decode_end(INT64_MAX), + _start_index(0), + _end_index(0), + _start_count(0), + _end_count(0), _show_hide_mapper(this), _popup_form(NULL), _popup() @@ -169,8 +174,31 @@ void DecodeTrace::paint_back(QPainter &p, int left, int right) QPen pen(Signal::dsGray); pen.setStyle(Qt::DotLine); p.setPen(pen); - const double sigY = get_y() - (_signalHeight - _view->get_signalHeight())*0.5; + const double sigY = get_y() - (_totalHeight - _view->get_signalHeight())*0.5; p.drawLine(left, sigY, right, sigY); + + // --draw decode region control + const double samples_per_pixel = _session.get_device()->get_sample_rate() * _view->scale(); + const double startX = _decode_start/samples_per_pixel - (_view->offset() / _view->scale()); + const double endX = _decode_end/samples_per_pixel - (_view->offset() / _view->scale()); + const double regionY = get_y() - _totalHeight*0.5 - ControlRectWidth; + + p.setBrush(Signal::dsBlue); + p.drawLine(startX, regionY, startX, regionY + _totalHeight + ControlRectWidth); + p.drawLine(endX, regionY, endX, regionY + _totalHeight + ControlRectWidth); + const QPointF start_points[] = { + QPointF(startX-ControlRectWidth, regionY), + QPointF(startX+ControlRectWidth, regionY), + QPointF(startX, regionY+ControlRectWidth) + }; + const QPointF end_points[] = { + QPointF(endX-ControlRectWidth, regionY), + QPointF(endX+ControlRectWidth, regionY), + QPointF(endX, regionY+ControlRectWidth) + }; + p.drawPolygon(start_points, countof(start_points)); + p.drawPolygon(end_points, countof(end_points)); + } void DecodeTrace::paint_mid(QPainter &p, int left, int right) @@ -203,22 +231,24 @@ void DecodeTrace::paint_mid(QPainter &p, int left, int right) const QString err = _decoder_stack->error_message(); if (!err.isEmpty()) { - //draw_unresolved_period(p, _view->get_signalHeight(), left, right, + //draw_unresolved_period(p, annotation_height, left, right, // samples_per_pixel, pixels_offset); draw_error(p, err, left, right); return; } // Draw the hatching - if (draw_unresolved_period(p, _view->get_signalHeight(), left, right)) + if (draw_unresolved_period(p, annotation_height, left, right)) return; // Iterate through the rows assert(_view); - int y = get_y() - (_signalHeight - _view->get_signalHeight())*0.5; + int y = get_y() - (_totalHeight - annotation_height)*0.5; assert(_decoder_stack); + const double decode_startX = _decode_start/samples_per_pixel - (_view->offset() / _view->scale()); + const double decode_endX = _decode_end/samples_per_pixel - (_view->offset() / _view->scale()); const std::vector< std::pair > rows(_decoder_stack->get_visible_rows()); for (size_t i = 0; i < rows.size(); i++) { @@ -226,8 +256,8 @@ void DecodeTrace::paint_mid(QPainter &p, int left, int right) const bool shown = rows[i].second; if (!shown && _decoder_stack->has_annotations(row)) { - draw_unshown_row(p, y, _view->get_signalHeight(), left, right); - y += _view->get_signalHeight(); + draw_unshown_row(p, y, annotation_height, decode_startX, decode_endX); + y += annotation_height; _cur_row_headings.push_back(row.title()); continue; } @@ -253,10 +283,10 @@ void DecodeTrace::paint_mid(QPainter &p, int left, int right) base_colour); } } else if (max_annWidth != 0){ - draw_nodetail(p, annotation_height, left, right, y, base_colour); + draw_nodetail(p, annotation_height, decode_startX, decode_endX, y, base_colour); } if (max_annWidth != 0) { - y += _view->get_signalHeight(); + y += annotation_height; _cur_row_headings.push_back(row.title()); } } @@ -272,7 +302,7 @@ void DecodeTrace::paint_fore(QPainter &p, int left, int right) for (size_t i = 0; i < _cur_row_headings.size(); i++) { - const int y = (i + 0.5) * row_height + get_y() - _signalHeight * 0.5; + const int y = (i + 0.5) * row_height + get_y() - _totalHeight * 0.5; p.setPen(QPen(Qt::NoPen)); p.setBrush(QApplication::palette().brush(QPalette::WindowText)); @@ -317,11 +347,13 @@ bool DecodeTrace::create_popup() if (QDialog::Accepted == _popup->exec()) { - BOOST_FOREACH(shared_ptr dec, + BOOST_FOREACH(boost::shared_ptr dec, _decoder_stack->stack()) { if (dec->commit()) { _decoder_stack->options_changed(true); + _decode_start = dec->decode_start(); + _decode_end = dec->decode_end(); ret = true; } } @@ -361,7 +393,7 @@ void DecodeTrace::populate_popup_form(QWidget *parent, QFormLayout *form) _probe_selectors.clear(); _decoder_forms.clear(); - const list< shared_ptr >& stack = _decoder_stack->stack(); + const list< boost::shared_ptr >& stack = _decoder_stack->stack(); if (stack.empty()) { @@ -372,10 +404,10 @@ void DecodeTrace::populate_popup_form(QWidget *parent, QFormLayout *form) } else { - list< shared_ptr >::const_iterator iter = + list< boost::shared_ptr >::const_iterator iter = stack.begin(); for (int i = 0; i < (int)stack.size(); i++, iter++) { - shared_ptr dec(*iter); + boost::shared_ptr dec(*iter); create_decoder_form(i, dec, parent, form); } @@ -383,6 +415,40 @@ void DecodeTrace::populate_popup_form(QWidget *parent, QFormLayout *form) tr("* Required channels"), parent)); } + // Add region combobox + _start_comboBox = new QComboBox(parent); + _end_comboBox = new QComboBox(parent); + _start_comboBox->addItem(RegionStart); + _end_comboBox->addItem(RegionEnd); + if (_view) { + int index = 1; + for(std::list::iterator i = _view->get_cursorList().begin(); + i != _view->get_cursorList().end(); i++) { + QString curCursor = tr("Cursor ")+QString::number(index); + _start_comboBox->addItem(curCursor); + _end_comboBox->addItem(curCursor); + index++; + } + } + if (_start_count > _start_comboBox->count()) + _start_index = 0; + if (_end_count > _end_comboBox->count()) + _end_index = 0; + _start_count = _start_comboBox->count(); + _end_count = _end_comboBox->count(); + + _start_comboBox->setCurrentIndex(_start_index); + _end_comboBox->setCurrentIndex(_end_index); + connect(_start_comboBox, SIGNAL(currentIndexChanged(int)), + this, SLOT(on_region_set(int))); + connect(_end_comboBox, SIGNAL(currentIndexChanged(int)), + this, SLOT(on_region_set(int))); + on_region_set(_start_index); + form->addRow(_start_comboBox, new QLabel( + tr("Decode Start From"))); + form->addRow(_end_comboBox, new QLabel( + tr("Decode End to"))); + // Add stacking button pv::widgets::DecoderMenu *const decoder_menu = new pv::widgets::DecoderMenu(parent); @@ -564,16 +630,16 @@ bool DecodeTrace::draw_unresolved_period(QPainter &p, int h, int left, assert(_decoder_stack); - shared_ptr data; - shared_ptr logic_signal; + boost::shared_ptr data; + boost::shared_ptr logic_signal; - //const int64_t sample_count = _session.get_device()->get_sample_limit(); - const int64_t sample_count = _decoder_stack->sample_count(); - if (sample_count == 0) + //const int64_t need_sample_count = _decoder_stack->sample_count(); + const uint64_t need_sample_count = _decode_end - _decode_start + 1; + if (need_sample_count == 0) return true; - const int64_t samples_decoded = _decoder_stack->samples_decoded(); - if (sample_count == samples_decoded) + const uint64_t samples_decoded = _decoder_stack->samples_decoded(); + if (need_sample_count == samples_decoded) return false; const int y = get_y(); @@ -591,7 +657,7 @@ bool DecodeTrace::draw_unresolved_period(QPainter &p, int h, int left, p.setBrush(QBrush(NoDecodeColour, Qt::Dense7Pattern)); p.drawRect(no_decode_rect); - const int progress100 = ceil(samples_decoded * 100.0 / sample_count); + const int progress100 = ceil(samples_decoded * 100.0 / need_sample_count); p.setPen(dsLightBlue); QFont font=p.font(); font.setPointSize(_view->get_signalHeight()*2/3); @@ -620,7 +686,7 @@ void DecodeTrace::draw_unshown_row(QPainter &p, int y, int h, int left, } void DecodeTrace::create_decoder_form(int index, - shared_ptr &dec, QWidget *parent, + boost::shared_ptr &dec, QWidget *parent, QFormLayout *form) { const GSList *l; @@ -672,7 +738,7 @@ void DecodeTrace::create_decoder_form(int index, } // Add the options - shared_ptr binding( + boost::shared_ptr binding( new prop::binding::DecoderOptions(_decoder_stack, dec)); binding->add_properties_to_form(decoder_form, true); @@ -683,16 +749,16 @@ void DecodeTrace::create_decoder_form(int index, } QComboBox* DecodeTrace::create_probe_selector( - QWidget *parent, const shared_ptr &dec, + QWidget *parent, const boost::shared_ptr &dec, const srd_channel *const pdch) { assert(dec); - const vector< shared_ptr > sigs(_session.get_signals()); + const vector< boost::shared_ptr > sigs(_session.get_signals()); assert(_decoder_stack); const map >::const_iterator probe_iter = + boost::shared_ptr >::const_iterator probe_iter = dec->channels().find(pdch); QComboBox *selector = new QComboBox(parent); @@ -703,7 +769,7 @@ QComboBox* DecodeTrace::create_probe_selector( selector->setCurrentIndex(0); for(size_t i = 0; i < sigs.size(); i++) { - const shared_ptr s(sigs[i]); + const boost::shared_ptr s(sigs[i]); assert(s); if (dynamic_pointer_cast(s) && s->enabled()) @@ -720,12 +786,12 @@ QComboBox* DecodeTrace::create_probe_selector( return selector; } -void DecodeTrace::commit_decoder_probes(shared_ptr &dec) +void DecodeTrace::commit_decoder_probes(boost::shared_ptr &dec) { assert(dec); - map > probe_map; - const vector< shared_ptr > sigs(_session.get_signals()); + map > probe_map; + const vector< boost::shared_ptr > sigs(_session.get_signals()); _index_list.clear(); BOOST_FOREACH(const ProbeSelector &s, _probe_selectors) @@ -737,7 +803,7 @@ void DecodeTrace::commit_decoder_probes(shared_ptr &dec) (LogicSignal*)s._combo->itemData( s._combo->currentIndex()).value(); - BOOST_FOREACH(shared_ptr sig, sigs) + BOOST_FOREACH(boost::shared_ptr sig, sigs) if(sig.get() == selection) { probe_map[s._pdch] = dynamic_pointer_cast(sig); @@ -752,7 +818,7 @@ void DecodeTrace::commit_decoder_probes(shared_ptr &dec) void DecodeTrace::commit_probes() { assert(_decoder_stack); - BOOST_FOREACH(shared_ptr dec, + BOOST_FOREACH(boost::shared_ptr dec, _decoder_stack->stack()) commit_decoder_probes(dec); @@ -787,7 +853,7 @@ void DecodeTrace::on_stack_decoder(srd_decoder *decoder) { assert(decoder); assert(_decoder_stack); - _decoder_stack->push(shared_ptr( + _decoder_stack->push(boost::shared_ptr( new data::decode::Decoder(decoder))); //_decoder_stack->begin_decode(); @@ -798,14 +864,14 @@ void DecodeTrace::on_show_hide_decoder(int index) { using pv::data::decode::Decoder; - const list< shared_ptr > stack(_decoder_stack->stack()); + const list< boost::shared_ptr > stack(_decoder_stack->stack()); // Find the decoder in the stack - list< shared_ptr >::const_iterator iter = stack.begin(); + list< boost::shared_ptr >::const_iterator iter = stack.begin(); for(int i = 0; i < index; i++, iter++) assert(iter != stack.end()); - shared_ptr dec = *iter; + boost::shared_ptr dec = *iter; assert(dec); const bool show = !dec->shown(); @@ -863,5 +929,62 @@ QRectF DecodeTrace::get_rect(DecodeSetRegions type, int y, int right) return QRectF(0, 0, 0, 0); } +void DecodeTrace::on_region_set(int index) +{ + (void)index; + const uint64_t last_samples = _session.get_device()->get_sample_limit() - 1; + const int index1 = _start_comboBox->currentIndex(); + const int index2 = _end_comboBox->currentIndex(); + uint64_t decode_start, decode_end; + + if (index1 == 0) { + decode_start = 0; + } else { + decode_start = _view->get_cursor_samples(index1-1); + } + if (index2 == 0) { + decode_end = last_samples; + } else { + decode_end = _view->get_cursor_samples(index2-1); + } + + if (decode_start > last_samples) + decode_start = 0; + if (decode_end > last_samples) + decode_end = last_samples; + + if (decode_start > decode_end) { + uint64_t tmp = decode_start; + decode_start = decode_end; + decode_end = tmp; + } + _start_index = index1; + _end_index = index2; + + BOOST_FOREACH(boost::shared_ptr dec, + _decoder_stack->stack()) { + dec->set_decode_region(decode_start, decode_end); + } +} + +void DecodeTrace::frame_ended() +{ + const uint64_t last_samples = _session.get_device()->get_sample_limit() - 1; + if (_decode_start > last_samples) { + _decode_start = 0; + _start_index = 0; + } + if (_end_index ==0 || + _decode_end > last_samples) { + _decode_end = last_samples; + _end_index = 0; + } + BOOST_FOREACH(boost::shared_ptr dec, + _decoder_stack->stack()) { + dec->set_decode_region(_decode_start, _decode_end); + dec->commit(); + } +} + } // namespace view } // namespace pv diff --git a/DSView/pv/view/decodetrace.h b/DSView/pv/view/decodetrace.h index 03cd6910..32f17bd9 100644 --- a/DSView/pv/view/decodetrace.h +++ b/DSView/pv/view/decodetrace.h @@ -89,6 +89,10 @@ private: static const QColor OutlineColours[16]; static const int DefaultFontSize = 8; + static const int ControlRectWidth = 5; + + static const QString RegionStart; + static const QString RegionEnd; public: DecodeTrace(pv::SigSession &session, @@ -132,6 +136,11 @@ public: QRectF get_rect(DecodeSetRegions type, int y, int right); + /** + * decode region + **/ + void frame_ended(); + protected: void paint_type_options(QPainter &p, int right, const QPoint pt); @@ -191,11 +200,16 @@ private slots: void on_decode_done(); + void on_region_set(int index); + private: pv::SigSession &_session; boost::shared_ptr _decoder_stack; uint64_t _decode_start, _decode_end; + int _start_index, _end_index; + int _start_count, _end_count; + QComboBox *_start_comboBox, *_end_comboBox; std::list< boost::shared_ptr > _bindings; diff --git a/DSView/pv/view/groupsignal.cpp b/DSView/pv/view/groupsignal.cpp index d5402acb..0c674dfd 100644 --- a/DSView/pv/view/groupsignal.cpp +++ b/DSView/pv/view/groupsignal.cpp @@ -51,7 +51,7 @@ GroupSignal::GroupSignal(QString name, boost::shared_ptr data, _data(data) { _colour = SignalColours[probe_index_list.front() % countof(SignalColours)]; - _scale = _signalHeight * 1.0f / std::pow(2.0, static_cast(probe_index_list.size())); + _scale = _totalHeight * 1.0f / std::pow(2.0, static_cast(probe_index_list.size())); } GroupSignal::~GroupSignal() @@ -79,12 +79,12 @@ void GroupSignal::paint_mid(QPainter &p, int left, int right) assert(_view); assert(right >= left); - const int y = get_y() + _signalHeight * 0.5; + const int y = get_y() + _totalHeight * 0.5; const double scale = _view->scale(); assert(scale > 0); const double offset = _view->offset(); - _scale = _signalHeight * 1.0f / std::pow(2.0, static_cast(_index_list.size())); + _scale = _totalHeight * 1.0f / std::pow(2.0, static_cast(_index_list.size())); const deque< boost::shared_ptr > &snapshots = _data->get_snapshots(); diff --git a/DSView/pv/view/logicsignal.cpp b/DSView/pv/view/logicsignal.cpp index 3ae1c9dd..4bbe9ad0 100644 --- a/DSView/pv/view/logicsignal.cpp +++ b/DSView/pv/view/logicsignal.cpp @@ -148,12 +148,12 @@ void LogicSignal::paint_mid(QPainter &p, int left, int right) assert(_view); assert(right >= left); - const int y = get_y() + _signalHeight * 0.5; + const int y = get_y() + _totalHeight * 0.5; const double scale = _view->scale(); assert(scale > 0); const double offset = _view->offset(); - const float high_offset = y - _signalHeight + 0.5f; + const float high_offset = y - _totalHeight + 0.5f; const float low_offset = y + 0.5f; const deque< boost::shared_ptr > &snapshots = @@ -314,7 +314,7 @@ void LogicSignal::paint_type_options(QPainter &p, int right, const QPoint pt) bool LogicSignal::measure(const QPointF &p, uint64_t &index0, uint64_t &index1, uint64_t &index2) const { const float gap = abs(p.y() - get_y()); - if (gap < get_signalHeight() * 0.5) { + if (gap < get_totalHeight() * 0.5) { const deque< boost::shared_ptr > &snapshots = _data->get_snapshots(); if (snapshots.empty()) @@ -359,7 +359,7 @@ bool LogicSignal::edges(const QPointF &p, uint64_t start, uint64_t &rising, uint { uint64_t index, end; const float gap = abs(p.y() - get_y()); - if (gap < get_signalHeight() * 0.5) { + if (gap < get_totalHeight() * 0.5) { const deque< boost::shared_ptr > &snapshots = _data->get_snapshots(); if (snapshots.empty()) diff --git a/DSView/pv/view/trace.cpp b/DSView/pv/view/trace.cpp index 92b4f210..52d522d5 100644 --- a/DSView/pv/view/trace.cpp +++ b/DSView/pv/view/trace.cpp @@ -59,7 +59,7 @@ Trace::Trace(QString name, uint16_t index, int type) : _v_offset(INT_MAX), _type(type), _sec_index(0), - _signalHeight(30) + _totalHeight(30) { _index_list.push_back(index); } @@ -71,7 +71,7 @@ Trace::Trace(QString name, std::list index_list, int type, int sec_index) : _type(type), _index_list(index_list), _sec_index(sec_index), - _signalHeight(30) + _totalHeight(30) { } @@ -84,7 +84,7 @@ Trace::Trace(const Trace &t) : _index_list(t._index_list), _sec_index(t._sec_index), _old_v_offset(t._old_v_offset), - _signalHeight(t._signalHeight), + _totalHeight(t._totalHeight), _text_size(t._text_size) { } @@ -167,14 +167,14 @@ int Trace::get_zeroPos() return _v_offset - _view->v_offset(); } -int Trace::get_signalHeight() const +int Trace::get_totalHeight() const { - return _signalHeight; + return _totalHeight; } -void Trace::set_signalHeight(int height) +void Trace::set_totalHeight(int height) { - _signalHeight = height; + _totalHeight = height; } void Trace::set_view(pv::view::View *view) @@ -183,6 +183,11 @@ void Trace::set_view(pv::view::View *view) _view = view; } +pv::view::View* Trace::get_view() const +{ + return _view; +} + void Trace::paint_back(QPainter &p, int left, int right) { QPen pen(Signal::dsGray); diff --git a/DSView/pv/view/trace.h b/DSView/pv/view/trace.h index ef3d31f3..329400bd 100644 --- a/DSView/pv/view/trace.h +++ b/DSView/pv/view/trace.h @@ -127,12 +127,12 @@ public: /** * Gets the height of this signal. */ - int get_signalHeight() const; + int get_totalHeight() const; /** * Sets the height of this signal. */ - void set_signalHeight(int height); + void set_totalHeight(int height); /** * Geom @@ -159,6 +159,7 @@ public: virtual bool enabled() const = 0; virtual void set_view(pv::view::View *view); + pv::view::View* get_view() const; /** * Paints the background layer of the trace with a QPainter @@ -291,7 +292,7 @@ protected: std::list _index_list; int _sec_index; int _old_v_offset; - int _signalHeight; + int _totalHeight; QSizeF _text_size; }; diff --git a/DSView/pv/view/view.cpp b/DSView/pv/view/view.cpp index 3965524b..eee4c438 100644 --- a/DSView/pv/view/view.cpp +++ b/DSView/pv/view/view.cpp @@ -501,7 +501,7 @@ void View::signals_changed() BOOST_FOREACH(boost::shared_ptr t, traces) { t->set_view(this); const double traceHeight = _signalHeight*t->rows_size(); - t->set_signalHeight((int)traceHeight); + t->set_totalHeight((int)traceHeight); t->set_v_offset(next_v_offset + 0.5 * traceHeight + SignalMargin); next_v_offset += traceHeight + 2 * SignalMargin; } diff --git a/DSView/pv/view/viewport.cpp b/DSView/pv/view/viewport.cpp index dfa94970..2f70b23e 100644 --- a/DSView/pv/view/viewport.cpp +++ b/DSView/pv/view/viewport.cpp @@ -33,6 +33,7 @@ #include "../data/logicsnapshot.h" #include "../sigsession.h" #include "../dialogs/dsomeasure.h" +#include "decodetrace.h" #include #include @@ -55,8 +56,7 @@ Viewport::Viewport(View &parent) : QWidget(&parent), _view(parent), _total_receive_len(0), - _zoom_rect_visible(false), - _measure_shown(false), + _action_type(NO_ACTION), _measure_type(NO_MEASURE), _cur_sample(0), _nxt_sample(1), @@ -65,10 +65,8 @@ Viewport::Viewport(View &parent) : _cur_midY(0), _hover_index(0), _hover_hit(false), - _dso_xm(false), - _dso_xm_stage(0), - _dso_ym(false), - _dso_ym_done(false) + _dso_xm_valid(false), + _dso_ym_valid(false) { setMouseTracking(true); setAutoFillBackground(true); @@ -105,7 +103,7 @@ int Viewport::get_total_height() const const vector< boost::shared_ptr > traces(_view.get_traces()); BOOST_FOREACH(const boost::shared_ptr t, traces) { assert(t); - h += (int)(t->get_signalHeight()); + h += (int)(t->get_totalHeight()); } h += 2 * View::SignalMargin; @@ -220,16 +218,14 @@ void Viewport::paintSignals(QPainter &p) } // plot zoom rect - if (_zoom_rect_visible) { + if (_action_type == LOGIC_ZOOM) { p.setPen(Qt::NoPen); p.setBrush(Trace::dsLightBlue); - p.drawRect(_zoom_rect); + p.drawRect(QRectF(_mouse_down_point, _mouse_point)); } //plot measure arrow - if (_measure_shown) { - paintMeasure(p); - } + paintMeasure(p); } void Viewport::paintProgress(QPainter &p) @@ -369,30 +365,10 @@ void Viewport::mousePressEvent(QMouseEvent *event) _mouse_down_point = event->pos(); _mouse_down_offset = _view.offset(); - _measure_shown = _dso_xm || _dso_ym; - //_dso_xm = false; _drag_strength = 0; _time.start(); - if (event->buttons() & Qt::LeftButton) { - if (_view.cursors_shown()) { - list::iterator i = _view.get_cursorList().begin(); - double cursorX; - const double samples_per_pixel = _view.session().get_device()->get_sample_rate() * _view.scale(); - while (i != _view.get_cursorList().end()) { - cursorX = (*i)->index()/samples_per_pixel - (_view.offset() / _view.scale()); - if ((*i)->grabbed()) { - _view.get_ruler()->rel_grabbed_cursor(); - } else if (qAbs(cursorX - event->pos().x()) <= HitCursorMargin) { - _view.get_ruler()->set_grabbed_cursor(*i); - _measure_type = LOGIC_CURS; - break; - } - i++; - } - - } - + if (event->button() == Qt::LeftButton) { const vector< boost::shared_ptr > sigs(_view.session().get_signals()); BOOST_FOREACH(const boost::shared_ptr s, sigs) { assert(s); @@ -400,10 +376,7 @@ void Viewport::mousePressEvent(QMouseEvent *event) continue; boost::shared_ptr dsoSig; if (dsoSig = dynamic_pointer_cast(s)) { - if (dsoSig->get_trig_rect(0, _view.get_view_width()).contains(_mouse_point)) { - _drag_sig = s; - break; - } else if (dsoSig->get_ms_show_hover()) { + if (dsoSig->get_ms_show_hover()) { dsoSig->set_ms_show(!dsoSig->get_ms_show()); break; } else if (dsoSig->get_ms_gear_hover()) { @@ -414,51 +387,20 @@ void Viewport::mousePressEvent(QMouseEvent *event) } } - if (_measure_type == LOGIC_FREQ) - _measure_type = NO_MEASURE; - update(); } - if (_hover_hit && (event->buttons() & Qt::RightButton)) { - _view.add_cursor(view::Ruler::CursorColor[_view.get_cursorList().size() % 8], _hover_index); - _view.show_cursors(true); - _hover_hit = false; - } else if (_hover_hit && (event->buttons() & Qt::LeftButton)) { - _dso_ym = true; - _dso_ym_done = false; - _dso_ym_sig_index = _hover_sig_index; - _dso_ym_sig_value = _hover_sig_value; - _dso_ym_index = _hover_index; - _dso_ym_start = event->pos().y(); - } else if (_dso_ym && !_dso_ym_done && (event->buttons() & Qt::LeftButton)) { - _dso_ym_end = event->pos().y(); - _dso_ym_done = true; - } else if (_dso_ym && !_dso_ym_done && (event->buttons() & Qt::RightButton)) { - _dso_ym = false; - _dso_ym_done = false; - } else if (_dso_xm && _dso_xm_stage < DsoMeasureStages && (event->buttons() & Qt::RightButton)) { - _dso_xm = false; - _measure_shown = _dso_ym; - _dso_xm_stage = 0; - _measure_type = NO_MEASURE; - _mm_width = "#####"; - _mm_period = "#####"; - _mm_freq = "#####"; - _mm_duty = "#####"; - mouse_measure(); - } else if (event->buttons() & Qt::LeftButton) { - if (_dso_xm_stage > 0 && _dso_xm_stage < DsoMeasureStages) { - const uint64_t sample_rate = _view.session().get_device()->get_sample_rate(); - const double scale = _view.scale(); - const double samples_per_pixel = sample_rate * scale; - _dso_xm_index[_dso_xm_stage] = event->pos().x() * samples_per_pixel + _view.offset() * sample_rate; - for(int i = _dso_xm_stage; i > 0; i--) { - const uint64_t max_index = max(_dso_xm_index[i-1], _dso_xm_index[i]); - _dso_xm_index[i-1] = min(_dso_xm_index[i-1], _dso_xm_index[i]); - _dso_xm_index[i] = max_index; + if (_action_type == NO_ACTION && + event->button() == Qt::RightButton && + _view.session().get_capture_state() == SigSession::Stopped) { + if (_view.session().get_device()->dev_inst()->mode == LOGIC) { + _action_type = LOGIC_ZOOM; + } else if (_view.session().get_device()->dev_inst()->mode == DSO) { + if (_hover_hit) { + uint64_t index = (_view.offset() + (event->pos().x() + 0.5) * _view.scale()) * _view.session().get_device()->get_sample_rate(); + _view.add_cursor(view::Ruler::CursorColor[_view.get_cursorList().size() % 8], index); + _view.show_cursors(true); } - _dso_xm_stage = (_dso_xm_stage + 1) % (DsoMeasureStages + 1); } } } @@ -467,41 +409,41 @@ void Viewport::mouseMoveEvent(QMouseEvent *event) { assert(event); _hover_hit = false; - if (!_dso_xm && (_dso_ym_done || !_dso_ym) && - event->buttons() & Qt::RightButton) { - _zoom_rect = QRectF(_mouse_down_point, event->pos()); - _zoom_rect_visible = true; - } if (event->buttons() & Qt::LeftButton) { - if (_drag_sig) { - boost::shared_ptr dsoSig; - if (dsoSig = dynamic_pointer_cast(_drag_sig)) - dsoSig->set_trig_vpos(event->pos().y()); - } else { - _view.set_scale_offset(_view.scale(), - _mouse_down_offset + - (_mouse_down_point - event->pos()).x() * - _view.scale()); - _drag_strength = (_mouse_down_point - event->pos()).x(); - //measure(); - } + _view.set_scale_offset(_view.scale(), + _mouse_down_offset + + (_mouse_down_point - event->pos()).x() * + _view.scale()); + _drag_strength = (_mouse_down_point - event->pos()).x(); } if (!(event->buttons() || Qt::NoButton)) { - uint64_t sample_rate = _view.session().get_device()->get_sample_rate(); - TimeMarker* grabbed_marker = _view.get_ruler()->get_grabbed_cursor(); - if (_view.cursors_shown() && grabbed_marker) { - const double cur_time = _view.offset() + _view.hover_point().x() * _view.scale(); - const double pos = cur_time * sample_rate; - const double pos_delta = pos - (uint64_t)pos; - if ( pos_delta < 0.5) - grabbed_marker->set_index((uint64_t)floor(pos)); - else - grabbed_marker->set_index((uint64_t)ceil(pos)); + if (_action_type == DSO_TRIG_MOVE) { + if (_drag_sig) { + boost::shared_ptr dsoSig; + if (dsoSig = dynamic_pointer_cast(_drag_sig)) + dsoSig->set_trig_vpos(event->pos().y()); + } } - if (_dso_ym && !_dso_ym_done) + + if (_action_type == CURS_MOVE) { + uint64_t sample_rate = _view.session().get_device()->get_sample_rate(); + TimeMarker* grabbed_marker = _view.get_ruler()->get_grabbed_cursor(); + if (_view.cursors_shown() && grabbed_marker) { + const double cur_time = _view.offset() + _view.hover_point().x() * _view.scale(); + const double pos = cur_time * sample_rate; + const double pos_delta = pos - (uint64_t)pos; + if ( pos_delta < 0.5) + grabbed_marker->set_index((uint64_t)floor(pos)); + else + grabbed_marker->set_index((uint64_t)ceil(pos)); + } + } + + if (_action_type == DSO_YM) _dso_ym_end = event->pos().y(); + measure(); } @@ -513,56 +455,176 @@ void Viewport::mouseReleaseEvent(QMouseEvent *event) { assert(event); - if (_zoom_rect_visible) { - _zoom_rect_visible = false; - const double newOffset = _view.offset() + (min(event->pos().x(), _mouse_down_point.x()) + 0.5) * _view.scale(); - const double newScale = max(min(_view.scale() * abs(event->pos().x() - _mouse_down_point.x()) / _view.get_view_width(), - _view.get_maxscale()), _view.get_minscale()); - if (newScale != _view.scale()) - _view.set_scale_offset(newScale, newOffset); - } - - if(_drag_sig) - _drag_sig.reset(); - - if ((_measure_type != LOGIC_MOVE && _measure_type != LOGIC_CURS) && - _view.session().get_device()->dev_inst()->mode == LOGIC && - _mouse_down_point.x() == event->pos().x() && - event->button() & Qt::LeftButton) { - if (_measure_type == LOGIC_EDGE) { - _measure_type = NO_MEASURE; - _measure_shown = false; - _edge_rising = 0; - _edge_falling = 0; - } else { - _measure_type = LOGIC_EDGE; - _edge_start = (_view.offset() + (event->pos().x() + 0.5) * _view.scale()) * _view.session().get_device()->get_sample_rate(); + if ((_action_type == NO_ACTION) && + (event->button() == Qt::LeftButton)) { + // priority 0 + if (_action_type == NO_ACTION && _view.cursors_shown()) { + list::iterator i = _view.get_cursorList().begin(); + double cursorX; + const double samples_per_pixel = _view.session().get_device()->get_sample_rate() * _view.scale(); + while (i != _view.get_cursorList().end()) { + cursorX = (*i)->index()/samples_per_pixel - (_view.offset() / _view.scale()); + if ((*i)->grabbed()) { + _view.get_ruler()->rel_grabbed_cursor(); + } else if (qAbs(cursorX - event->pos().x()) <= HitCursorMargin) { + _view.get_ruler()->set_grabbed_cursor(*i); + _action_type = CURS_MOVE; + break; + } + i++; + } } - } - if (_view.session().get_device()->dev_inst()->mode == LOGIC && - (_measure_type == NO_MEASURE || _measure_type == LOGIC_MOVE)) { - const double strength = _drag_strength*DragTimerInterval*1.0/_time.elapsed(); - if (_time.elapsed() < 200 && - abs(_drag_strength) < MinorDragOffsetUp && - abs(strength) > MinorDragRateUp) { - _drag_strength = _drag_strength; - _drag_timer.start(DragTimerInterval); - _measure_type = LOGIC_MOVE; - } else if (_time.elapsed() < 200 && - abs(strength) > DragTimerInterval) { - _drag_strength = strength * 5; - _drag_timer.start(DragTimerInterval); - _measure_type = LOGIC_MOVE; - } else { - _drag_strength = 0; - _drag_timer.stop(); - _measure_type = NO_MEASURE; + if (_view.session().get_device()->dev_inst()->mode == LOGIC && + _view.session().get_capture_state() == SigSession::Stopped) { + // priority 1 + if (_action_type == NO_ACTION) { + const double strength = _drag_strength*DragTimerInterval*1.0/_time.elapsed(); + if (_time.elapsed() < 200 && + abs(_drag_strength) < MinorDragOffsetUp && + abs(strength) > MinorDragRateUp) { + _drag_strength = _drag_strength; + _drag_timer.start(DragTimerInterval); + _action_type = LOGIC_MOVE; + } else if (_time.elapsed() < 200 && + abs(strength) > DragTimerInterval) { + _drag_strength = strength * 5; + _drag_timer.start(DragTimerInterval); + _action_type = LOGIC_MOVE; + } + } + + // priority 2 + if (_action_type == NO_ACTION) { + if (_mouse_down_point.x() == event->pos().x()) { + const vector< boost::shared_ptr > sigs(_view.session().get_signals()); + BOOST_FOREACH(const boost::shared_ptr s, sigs) { + assert(s); + if (abs(event->pos().y() - s->get_y()) < _view.get_signalHeight()) { + _action_type = LOGIC_EDGE; + _edge_start = (_view.offset() + (event->pos().x() + 0.5) * _view.scale()) * _view.session().get_device()->get_sample_rate(); + break; + } + } + } + } + } else if (_view.session().get_device()->dev_inst()->mode == DSO) { + // priority 0 + if (_action_type == NO_ACTION && _hover_hit) { + _action_type = DSO_YM; + _dso_ym_valid = true; + _dso_ym_sig_index = _hover_sig_index; + _dso_ym_sig_value = _hover_sig_value; + _dso_ym_index = _hover_index; + _dso_ym_start = event->pos().y(); + } + + // priority 1 + if (_action_type == NO_ACTION) { + const vector< boost::shared_ptr > sigs(_view.session().get_signals()); + BOOST_FOREACH(const boost::shared_ptr s, sigs) { + assert(s); + if (!s->enabled()) + continue; + boost::shared_ptr dsoSig; + if (dsoSig = dynamic_pointer_cast(s)) { + if (dsoSig->get_trig_rect(0, _view.get_view_width()).contains(_mouse_point)) { + _drag_sig = s; + _action_type = DSO_TRIG_MOVE; + break; + } + } + } + } } - } + } else if (_action_type == DSO_YM) { + if (event->button() == Qt::LeftButton) { + _dso_ym_end = event->pos().y(); + _action_type = NO_ACTION; + } else if (event->button() == Qt::RightButton) { + _action_type = NO_ACTION; + _dso_ym_valid = false; + } + } else if (_action_type == DSO_TRIG_MOVE) { + if (event->button() == Qt::LeftButton) { + _drag_sig.reset(); + _action_type = NO_ACTION; + } + } else if (_action_type == DSO_XM_STEP0) { + if (event->button() == Qt::LeftButton) { + _action_type = DSO_XM_STEP1; + _dso_xm_valid = true; + } + } else if (_action_type == DSO_XM_STEP1) { + if (event->button() == Qt::LeftButton) { + const uint64_t sample_rate = _view.session().get_device()->get_sample_rate(); + const double scale = _view.scale(); + const double samples_per_pixel = sample_rate * scale; - if (!_view.get_ruler()->get_grabbed_cursor() && _measure_type == LOGIC_CURS) - _measure_type = NO_MEASURE; + _dso_xm_index[1] = event->pos().x() * samples_per_pixel + _view.offset() * sample_rate; + const uint64_t max_index = max(_dso_xm_index[0], _dso_xm_index[1]); + _dso_xm_index[0] = min(_dso_xm_index[0], _dso_xm_index[1]); + _dso_xm_index[1] = max_index; + + _action_type = DSO_XM_STEP2; + } else if (event->button() == Qt::RightButton) { + _action_type = NO_ACTION; + _dso_xm_valid = false; + _mm_width = "#####"; + _mm_period = "#####"; + _mm_freq = "#####"; + _mm_duty = "#####"; + measure_updated(); + } + } else if (_action_type == DSO_XM_STEP2) { + if (event->button() == Qt::LeftButton) { + const uint64_t sample_rate = _view.session().get_device()->get_sample_rate(); + const double scale = _view.scale(); + const double samples_per_pixel = sample_rate * scale; + _dso_xm_index[2] = event->pos().x() * samples_per_pixel + _view.offset() * sample_rate; + const uint64_t max_index = max(_dso_xm_index[1], _dso_xm_index[2]); + _dso_xm_index[1] = min(_dso_xm_index[1], _dso_xm_index[2]); + _dso_xm_index[2] = max_index; + + _action_type = NO_ACTION; + } else if (event->button() == Qt::RightButton) { + _action_type = NO_ACTION; + _dso_xm_valid = false; + _mm_width = "#####"; + _mm_period = "#####"; + _mm_freq = "#####"; + _mm_duty = "#####"; + measure_updated(); + } + } else if (_action_type == CURS_MOVE) { + _action_type = NO_ACTION; + if (_view.cursors_shown()) { + list::iterator i = _view.get_cursorList().begin(); + while (i != _view.get_cursorList().end()) { + if ((*i)->grabbed()) { + _view.get_ruler()->rel_grabbed_cursor(); + } + i++; + } + } + } else if (_action_type == LOGIC_EDGE) { + _action_type = NO_ACTION; + _edge_rising = 0; + _edge_falling = 0; + } else if (_action_type == LOGIC_MOVE) { + _drag_strength = 0; + _drag_timer.stop(); + _action_type = NO_ACTION; + } else if (_action_type == LOGIC_ZOOM) { + if (event->pos().x() != _mouse_down_point.x()) { + const double newOffset = _view.offset() + (min(event->pos().x(), _mouse_down_point.x()) + 0.5) * _view.scale(); + const double newScale = max(min(_view.scale() * abs(event->pos().x() - _mouse_down_point.x()) / _view.get_view_width(), + _view.get_maxscale()), _view.get_minscale()); + if (newScale != _view.scale()) + _view.set_scale_offset(newScale, newOffset); + } + _action_type = NO_ACTION; + } update(); } @@ -572,13 +634,14 @@ void Viewport::mouseDoubleClickEvent(QMouseEvent *event) assert (event); (void)event; - if (_view.session().get_device()->dev_inst()->mode == LOGIC) { - if (event->button() & Qt::RightButton) { + if (_view.session().get_device()->dev_inst()->mode == LOGIC && + _view.session().get_capture_state() == SigSession::Stopped) { + if (event->button() == Qt::RightButton) { if (_view.scale() == _view.get_maxscale()) _view.set_preScale_preOffset(); else _view.set_scale_offset(_view.get_maxscale(), 0); - } else if (event->button() & Qt::LeftButton) { + } else if (event->button() == Qt::LeftButton) { uint64_t index = (_view.offset() + (event->pos().x() + 0.5) * _view.scale()) * _view.session().get_device()->get_sample_rate(); _view.add_cursor(view::Ruler::CursorColor[_view.get_cursorList().size() % 8], index); _view.show_cursors(true); @@ -586,27 +649,23 @@ void Viewport::mouseDoubleClickEvent(QMouseEvent *event) update(); } else if (_view.session().get_device()->dev_inst()->mode == DSO && _view.session().get_capture_state() != SigSession::Init && - event->button() & Qt::LeftButton) { - if (_dso_xm_stage == 0) { - uint64_t sample_rate = _view.session().get_device()->get_sample_rate(); - double scale = _view.scale(); - const double samples_per_pixel = sample_rate * scale; - _dso_xm_index[0] = event->pos().x() * samples_per_pixel + - _view.offset() * sample_rate;; - _dso_xm_stage = 1; - _dso_xm_y = event->pos().y(); - _dso_xm = true; - _measure_type = DSO_FREQ; - _measure_shown = true; - } else if (_dso_xm_stage == DsoMeasureStages) { - _dso_xm = false; - _measure_shown = _dso_ym; - _dso_xm_stage = 0; + event->button() == Qt::LeftButton) { + if (_dso_xm_valid) { + _dso_xm_valid = false; + _action_type = NO_ACTION; _mm_width = "#####"; _mm_period = "#####"; _mm_freq = "#####"; _mm_duty = "#####"; - mouse_measure(); + measure_updated(); + } else if (_action_type == NO_ACTION) { + uint64_t sample_rate = _view.session().get_device()->get_sample_rate(); + double scale = _view.scale(); + const double samples_per_pixel = sample_rate * scale; + _dso_xm_index[0] = event->pos().x() * samples_per_pixel + + _view.offset() * sample_rate; + _dso_xm_y = event->pos().y(); + _action_type = DSO_XM_STEP0; } } } @@ -631,13 +690,41 @@ void Viewport::wheelEvent(QWheelEvent *event) void Viewport::leaveEvent(QEvent *) { - _measure_shown = _dso_xm || _dso_ym; _mouse_point = QPoint(-1, -1); //_view.show_cursors(false); - if (_measure_type == LOGIC_EDGE || _measure_type == LOGIC_MOVE) { - _measure_type = NO_MEASURE; - _measure_shown = false; + + if (_action_type == CURS_MOVE) { + if (_view.cursors_shown()) { + list::iterator i = _view.get_cursorList().begin(); + while (i != _view.get_cursorList().end()) { + if ((*i)->grabbed()) { + _view.get_ruler()->rel_grabbed_cursor(); + } + i++; + } + } + } else if (_action_type == LOGIC_EDGE) { + _edge_rising = 0; + _edge_falling = 0; + } else if (_action_type == LOGIC_MOVE) { + _drag_strength = 0; + _drag_timer.stop(); + } else if (_action_type == DSO_TRIG_MOVE) { + _drag_sig.reset(); + } else if (_action_type == DSO_XM_STEP1 || _action_type == DSO_XM_STEP2) { + _dso_xm_valid = false; + _mm_width = "#####"; + _mm_period = "#####"; + _mm_freq = "#####"; + _mm_duty = "#####"; + measure_updated(); + } else if (_action_type == DSO_YM) { + _dso_ym_valid = false; } + + if (_action_type != NO_ACTION) + _action_type = NO_ACTION; + update(); } @@ -669,11 +756,7 @@ void Viewport::clear_measure() void Viewport::measure() { - if ((_view.session().get_device()->dev_inst()->mode == LOGIC && - _view.session().get_capture_state() == SigSession::Running) || - _drag_strength != 0) - return; - _measure_shown = _dso_xm || _dso_ym; + _measure_type = NO_MEASURE; const uint64_t sample_rate = _view.session().get_device()->get_sample_rate(); const vector< boost::shared_ptr > sigs(_view.session().get_signals()); BOOST_FOREACH(const boost::shared_ptr s, sigs) { @@ -681,61 +764,64 @@ void Viewport::measure() boost::shared_ptr logicSig; boost::shared_ptr dsoSig; if (logicSig = dynamic_pointer_cast(s)) { - if (_measure_type != LOGIC_EDGE && - logicSig->measure(_view.hover_point(), _cur_sample, _nxt_sample, _thd_sample)) { - _measure_shown = true; - _measure_type = LOGIC_FREQ; + if (_action_type == NO_ACTION) { + if (logicSig->measure(_view.hover_point(), _cur_sample, _nxt_sample, _thd_sample)) { + _measure_type = LOGIC_FREQ; - _mm_width = _view.get_ruler()->format_real_time(_nxt_sample - _cur_sample, sample_rate); - _mm_period = _thd_sample != 0 ? _view.get_ruler()->format_real_time(_thd_sample - _cur_sample, sample_rate) : "#####"; - _mm_freq = _thd_sample != 0 ? _view.get_ruler()->format_real_freq(_thd_sample - _cur_sample, sample_rate) : "#####"; + _mm_width = _view.get_ruler()->format_real_time(_nxt_sample - _cur_sample, sample_rate); + _mm_period = _thd_sample != 0 ? _view.get_ruler()->format_real_time(_thd_sample - _cur_sample, sample_rate) : "#####"; + _mm_freq = _thd_sample != 0 ? _view.get_ruler()->format_real_freq(_thd_sample - _cur_sample, sample_rate) : "#####"; - const double pixels_offset = _view.offset() / _view.scale(); - const double samples_per_pixel = sample_rate * _view.scale(); - _cur_preX = _cur_sample / samples_per_pixel - pixels_offset; - _cur_aftX = _nxt_sample / samples_per_pixel - pixels_offset; - _cur_thdX = _thd_sample / samples_per_pixel - pixels_offset; - _cur_midY = logicSig->get_y(); + const double pixels_offset = _view.offset() / _view.scale(); + const double samples_per_pixel = sample_rate * _view.scale(); + _cur_preX = _cur_sample / samples_per_pixel - pixels_offset; + _cur_aftX = _nxt_sample / samples_per_pixel - pixels_offset; + _cur_thdX = _thd_sample / samples_per_pixel - pixels_offset; + _cur_midY = logicSig->get_y(); - _mm_duty = _thd_sample != 0 ? QString::number((_nxt_sample - _cur_sample) * 100.0 / (_thd_sample - _cur_sample), 'f', 2)+"%" : - "#####"; - mouse_measure(); - break; - } else if (_measure_type == LOGIC_EDGE && - logicSig->edges(_view.hover_point(), _edge_start, _edge_rising, _edge_falling)) { - _measure_shown = true; + _mm_duty = _thd_sample != 0 ? QString::number((_nxt_sample - _cur_sample) * 100.0 / (_thd_sample - _cur_sample), 'f', 2)+"%" : + "#####"; + break; + } else { + _measure_type = NO_MEASURE; + _mm_width = "#####"; + _mm_period = "#####"; + _mm_freq = "#####"; + _mm_duty = "#####"; + } + } else if (_action_type == LOGIC_EDGE) { + if (logicSig->edges(_view.hover_point(), _edge_start, _edge_rising, _edge_falling)) { + const double pixels_offset = _view.offset() / _view.scale(); + const double samples_per_pixel = sample_rate * _view.scale(); + _cur_preX = _edge_start / samples_per_pixel - pixels_offset; + _cur_aftX = _view.hover_point().x(); + _cur_midY = logicSig->get_y() - logicSig->get_totalHeight()/2 - 5; - const double pixels_offset = _view.offset() / _view.scale(); - const double samples_per_pixel = sample_rate * _view.scale(); - _cur_preX = _edge_start / samples_per_pixel - pixels_offset; - _cur_aftX = _view.hover_point().x(); - _cur_midY = logicSig->get_y() - logicSig->get_signalHeight()/2 - 5; + _em_rising = "Rising: " + QString::number(_edge_rising); + _em_falling = "Falling: " + QString::number(_edge_falling); + _em_edges = "Edges: " + QString::number(_edge_rising + _edge_falling); - _em_rising = "Rising: " + QString::number(_edge_rising); - _em_falling = "Falling: " + QString::number(_edge_falling); - _em_edges = "Edges: " + QString::number(_edge_rising + _edge_falling); - - break; - } else { - _mm_width = "#####"; - _mm_period = "#####"; - _mm_freq = "#####"; - _mm_duty = "#####"; + break; + } } - mouse_measure(); } else if (dsoSig = dynamic_pointer_cast(s)) { if (_measure_en && dsoSig->measure(_view.hover_point())) { - _measure_shown = true; - _measure_type = DSO_FREQ; + _measure_type = DSO_VALUE; + break; + } else { + _measure_type = NO_MEASURE; } } } + if (_measure_type != NO_MEASURE) + measure_updated(); } void Viewport::paintMeasure(QPainter &p) { _hover_hit = false; - if (_measure_type == LOGIC_FREQ) { + if (_action_type == NO_ACTION && + _measure_type == LOGIC_FREQ) { p.setPen(QColor(17, 133, 209, 255)); p.drawLine(QLineF(_cur_preX, _cur_midY, _cur_aftX, _cur_midY)); p.drawLine(QLineF(_cur_preX, _cur_midY, _cur_preX + 2, _cur_midY - 2)); @@ -788,45 +874,11 @@ void Viewport::paintMeasure(QPainter &p) p.drawText(measure4_rect, Qt::AlignRight | Qt::AlignVCenter, tr("Duty Cycle: ") + _mm_duty); } - } else if (_measure_type == LOGIC_EDGE) { - p.setPen(QColor(17, 133, 209, 255)); + } - p.drawLine(QLineF(_cur_preX, _cur_midY-5, _cur_preX, _cur_midY+5)); - p.drawLine(QLineF(_cur_aftX, _cur_midY-5, _cur_aftX, _cur_midY+5)); - p.drawLine(QLineF(_cur_preX, _cur_midY, _cur_aftX, _cur_midY)); - - int typical_width = p.boundingRect(0, 0, INT_MAX, INT_MAX, - Qt::AlignLeft | Qt::AlignTop, _em_edges).width(); - typical_width = max(typical_width, p.boundingRect(0, 0, INT_MAX, INT_MAX, - Qt::AlignLeft | Qt::AlignTop, _em_rising).width()); - typical_width = max(typical_width, p.boundingRect(0, 0, INT_MAX, INT_MAX, - Qt::AlignLeft | Qt::AlignTop, _em_falling).width()); - - typical_width = typical_width + 30; - - const double width = _view.get_view_width(); - const double height = _view.viewport()->height(); - const double left = _view.hover_point().x(); - const double top = _view.hover_point().y(); - const double right = left + typical_width; - const double bottom = top + 60; - QPointF org_pos = QPointF(right > width ? left - typical_width : left, bottom > height ? top - 80 : top); - QRectF measure_rect = QRectF(org_pos.x(), org_pos.y(), (double)typical_width, 60.0); - QRectF measure1_rect = QRectF(org_pos.x(), org_pos.y(), (double)typical_width, 20.0); - QRectF measure2_rect = QRectF(org_pos.x(), org_pos.y()+20, (double)typical_width, 20.0); - QRectF measure3_rect = QRectF(org_pos.x(), org_pos.y()+40, (double)typical_width, 20.0); - - p.setPen(Qt::NoPen); - p.setBrush(QColor(17, 133, 209, 150)); - p.drawRect(measure_rect); - - p.setPen(Qt::black); - p.drawText(measure1_rect, Qt::AlignRight | Qt::AlignVCenter, _em_edges); - p.drawText(measure2_rect, Qt::AlignRight | Qt::AlignVCenter, _em_rising); - p.drawText(measure3_rect, Qt::AlignRight | Qt::AlignVCenter, _em_falling); - - } else if (_measure_type == DSO_FREQ) { - const vector< boost::shared_ptr > sigs(_view.session().get_signals()); + const vector< boost::shared_ptr > sigs(_view.session().get_signals()); + if (_action_type == NO_ACTION && + _measure_type == DSO_VALUE) { BOOST_FOREACH(const boost::shared_ptr s, sigs) { boost::shared_ptr dsoSig; if (dsoSig = dynamic_pointer_cast(s)) { @@ -883,132 +935,177 @@ void Viewport::paintMeasure(QPainter &p) } } } + } - // -- vertical value - if (_dso_ym) { - BOOST_FOREACH(const boost::shared_ptr s, sigs) { - boost::shared_ptr dsoSig; - if (dsoSig = dynamic_pointer_cast(s)) { - if (dsoSig->get_index() == _dso_ym_sig_index) { - p.setPen(QPen(dsoSig->get_colour(), 1, Qt::DotLine)); - const int text_height = p.boundingRect(0, 0, INT_MAX, INT_MAX, - Qt::AlignLeft | Qt::AlignTop, "W").height(); - const uint64_t sample_rate = _view.session().get_device()->get_sample_rate(); - const double x = (_dso_ym_index / (sample_rate * _view.scale())) - - _view.offset() /_view.scale(); - p.drawLine(x-10, _dso_ym_start, - x+10, _dso_ym_start); - p.drawLine(x, _dso_ym_start, - x, _dso_ym_end); - p.drawLine(0, _dso_ym_end, - _view.get_view_width(), _dso_ym_end); + if (_dso_ym_valid) { + BOOST_FOREACH(const boost::shared_ptr s, sigs) { + boost::shared_ptr dsoSig; + if (dsoSig = dynamic_pointer_cast(s)) { + if (dsoSig->get_index() == _dso_ym_sig_index) { + p.setPen(QPen(dsoSig->get_colour(), 1, Qt::DotLine)); + const int text_height = p.boundingRect(0, 0, INT_MAX, INT_MAX, + Qt::AlignLeft | Qt::AlignTop, "W").height(); + const uint64_t sample_rate = _view.session().get_device()->get_sample_rate(); + const double x = (_dso_ym_index / (sample_rate * _view.scale())) - + _view.offset() /_view.scale(); + p.drawLine(x-10, _dso_ym_start, + x+10, _dso_ym_start); + p.drawLine(x, _dso_ym_start, + x, _dso_ym_end); + p.drawLine(0, _dso_ym_end, + _view.get_view_width(), _dso_ym_end); - // -- vertical delta value - double hrate = (_dso_ym_start - _dso_ym_end) * 1.0f / _view.get_view_height(); - double value = hrate * dsoSig->get_vDialValue() * dsoSig->get_factor() * DS_CONF_DSO_VDIVS; - QString value_str = abs(value) > 1000 ? QString::number(value/1000.0, 'f', 2) + "V" : QString::number(value, 'f', 2) + "mV"; - int value_rect_width = p.boundingRect(0, 0, INT_MAX, INT_MAX, - Qt::AlignLeft | Qt::AlignVCenter, value_str).width(); - p.drawText(QRect(x+10, abs(_dso_ym_start+_dso_ym_end)/2, value_rect_width, text_height), - value_str); + // -- vertical delta value + double hrate = (_dso_ym_start - _dso_ym_end) * 1.0f / _view.get_view_height(); + double value = hrate * dsoSig->get_vDialValue() * dsoSig->get_factor() * DS_CONF_DSO_VDIVS; + QString value_str = abs(value) > 1000 ? QString::number(value/1000.0, 'f', 2) + "V" : QString::number(value, 'f', 2) + "mV"; + int value_rect_width = p.boundingRect(0, 0, INT_MAX, INT_MAX, + Qt::AlignLeft | Qt::AlignVCenter, value_str).width(); + p.drawText(QRect(x+10, abs(_dso_ym_start+_dso_ym_end)/2, value_rect_width, text_height), + value_str); - // -- start value - value_str = abs(_dso_ym_sig_value) > 1000 ? QString::number(_dso_ym_sig_value/1000.0, 'f', 2) + "V" : QString::number(_dso_ym_sig_value, 'f', 2) + "mV"; - value_rect_width = p.boundingRect(0, 0, INT_MAX, INT_MAX, - Qt::AlignLeft | Qt::AlignVCenter, value_str).width(); - int str_y = value > 0 ? _dso_ym_start : _dso_ym_start - text_height; - p.drawText(QRect(x-0.5*value_rect_width, str_y, value_rect_width, text_height), - value_str); + // -- start value + value_str = abs(_dso_ym_sig_value) > 1000 ? QString::number(_dso_ym_sig_value/1000.0, 'f', 2) + "V" : QString::number(_dso_ym_sig_value, 'f', 2) + "mV"; + value_rect_width = p.boundingRect(0, 0, INT_MAX, INT_MAX, + Qt::AlignLeft | Qt::AlignVCenter, value_str).width(); + int str_y = value > 0 ? _dso_ym_start : _dso_ym_start - text_height; + p.drawText(QRect(x-0.5*value_rect_width, str_y, value_rect_width, text_height), + value_str); - // -- end value - double end_value = _dso_ym_sig_value + value; - value_str = abs(end_value) > 1000 ? QString::number(end_value/1000.0, 'f', 2) + "V" : QString::number(end_value, 'f', 2) + "mV"; - value_rect_width = p.boundingRect(0, 0, INT_MAX, INT_MAX, - Qt::AlignLeft | Qt::AlignVCenter, value_str).width(); - str_y = value > 0 ? _dso_ym_end-text_height : _dso_ym_end; - p.drawText(QRect(x-0.5*value_rect_width, str_y, value_rect_width, text_height), - value_str); - break; - } + // -- end value + double end_value = _dso_ym_sig_value + value; + value_str = abs(end_value) > 1000 ? QString::number(end_value/1000.0, 'f', 2) + "V" : QString::number(end_value, 'f', 2) + "mV"; + value_rect_width = p.boundingRect(0, 0, INT_MAX, INT_MAX, + Qt::AlignLeft | Qt::AlignVCenter, value_str).width(); + str_y = value > 0 ? _dso_ym_end-text_height : _dso_ym_end; + p.drawText(QRect(x-0.5*value_rect_width, str_y, value_rect_width, text_height), + value_str); + break; } } } + } - // -- width/period/frequency/duty - if (_dso_xm) { - p.setPen(QPen(Qt::red, 1, Qt::DotLine)); - int measure_line_count = 6; - const int text_height = p.boundingRect(0, 0, INT_MAX, INT_MAX, - Qt::AlignLeft | Qt::AlignTop, "W").height(); - const uint64_t sample_rate = _view.session().get_device()->get_sample_rate(); - QLineF *line; - QLineF *const measure_lines = new QLineF[measure_line_count]; - line = measure_lines; - double x[DsoMeasureStages]; - for (int i = 0; i < _dso_xm_stage; i++) { - x[i] = (_dso_xm_index[i] / (sample_rate * _view.scale())) - - _view.offset() /_view.scale(); - } - measure_line_count = 0; - if (_dso_xm_stage > 0) { - *line++ = QLineF(x[0], _dso_xm_y - 10, - x[0], _dso_xm_y + 10); - measure_line_count += 1; - } - if (_dso_xm_stage > 1) { - *line++ = QLineF(x[1], _dso_xm_y - 10, - x[1], _dso_xm_y + 10); - *line++ = QLineF(x[0], _dso_xm_y, - x[1], _dso_xm_y); - _mm_width = _view.get_ruler()->format_real_time(_dso_xm_index[1] - _dso_xm_index[0], sample_rate); + if (_dso_xm_valid) { + p.setPen(QPen(Qt::red, 1, Qt::DotLine)); + int measure_line_count = 6; + const int text_height = p.boundingRect(0, 0, INT_MAX, INT_MAX, + Qt::AlignLeft | Qt::AlignTop, "W").height(); + const uint64_t sample_rate = _view.session().get_device()->get_sample_rate(); + QLineF *line; + QLineF *const measure_lines = new QLineF[measure_line_count]; + line = measure_lines; + double x[DsoMeasureStages]; + int dso_xm_stage = 0; + if (_action_type == DSO_XM_STEP1) + dso_xm_stage = 1; + else if(_action_type == DSO_XM_STEP2) + dso_xm_stage = 2; + else + dso_xm_stage = 3; - // -- width show - const QString w_ctr = "W="+_mm_width; - int w_rect_width = p.boundingRect(0, 0, INT_MAX, INT_MAX, - Qt::AlignLeft | Qt::AlignVCenter, w_ctr).width(); - p.drawText(QRect(x[0]+10, _dso_xm_y - text_height, w_rect_width, text_height), w_ctr); - measure_line_count += 2; - } - if (_dso_xm_stage > 2) { - *line++ = QLineF(x[0], _dso_xm_y + 20, - x[0], _dso_xm_y + 40); - *line++ = QLineF(x[0], _dso_xm_y + 30, - x[2], _dso_xm_y + 30); - *line++ = QLineF(x[2], _dso_xm_y + 20, - x[2], _dso_xm_y + 40); - _mm_period = _view.get_ruler()->format_real_time(_dso_xm_index[2] - _dso_xm_index[0], sample_rate); - _mm_freq = _view.get_ruler()->format_real_freq(_dso_xm_index[2] - _dso_xm_index[0], sample_rate); - _mm_duty = QString::number((_dso_xm_index[1] - _dso_xm_index[0]) * 100.0 / (_dso_xm_index[2] - _dso_xm_index[0]), 'f', 2)+"%"; - - // -- period show - const QString p_ctr = "P="+_mm_period; - int p_rect_width = p.boundingRect(0, 0, INT_MAX, INT_MAX, - Qt::AlignLeft | Qt::AlignVCenter, p_ctr).width(); - p.drawText(QRect(x[0]+10, _dso_xm_y + 30 - text_height, p_rect_width, text_height), p_ctr); - - // -- frequency show - const QString f_ctr = "F="+_mm_freq; - int f_rect_width = p.boundingRect(0, 0, INT_MAX, INT_MAX, - Qt::AlignLeft | Qt::AlignVCenter, f_ctr).width(); - p.drawText(QRect(x[0]+20 + p_rect_width, _dso_xm_y + 30 - text_height, f_rect_width, text_height), f_ctr); - - // -- duty show - const QString d_ctr = "D="+_mm_duty; - int d_rect_width = p.boundingRect(0, 0, INT_MAX, INT_MAX, - Qt::AlignLeft | Qt::AlignVCenter, d_ctr).width(); - p.drawText(QRect(x[1]+10, _dso_xm_y - 0.5*text_height, d_rect_width, text_height), d_ctr); - - measure_line_count += 3; - } - p.drawLines(measure_lines, measure_line_count); - if (_dso_xm_stage < DsoMeasureStages) { - p.drawLine(x[_dso_xm_stage-1], _dso_xm_y, - _mouse_point.x(), _dso_xm_y); - p.drawLine(_mouse_point.x(), 0, - _mouse_point.x(), _view.get_viewport()->height()); - } - mouse_measure(); + for (int i = 0; i < dso_xm_stage; i++) { + x[i] = (_dso_xm_index[i] / (sample_rate * _view.scale())) - + _view.offset() /_view.scale(); } + measure_line_count = 0; + if (dso_xm_stage > 0) { + *line++ = QLineF(x[0], _dso_xm_y - 10, + x[0], _dso_xm_y + 10); + measure_line_count += 1; + } + if (dso_xm_stage > 1) { + *line++ = QLineF(x[1], _dso_xm_y - 10, + x[1], _dso_xm_y + 10); + *line++ = QLineF(x[0], _dso_xm_y, + x[1], _dso_xm_y); + _mm_width = _view.get_ruler()->format_real_time(_dso_xm_index[1] - _dso_xm_index[0], sample_rate); + + // -- width show + const QString w_ctr = "W="+_mm_width; + int w_rect_width = p.boundingRect(0, 0, INT_MAX, INT_MAX, + Qt::AlignLeft | Qt::AlignVCenter, w_ctr).width(); + p.drawText(QRect(x[0]+10, _dso_xm_y - text_height, w_rect_width, text_height), w_ctr); + measure_line_count += 2; + } + if (dso_xm_stage > 2) { + *line++ = QLineF(x[0], _dso_xm_y + 20, + x[0], _dso_xm_y + 40); + *line++ = QLineF(x[0], _dso_xm_y + 30, + x[2], _dso_xm_y + 30); + *line++ = QLineF(x[2], _dso_xm_y + 20, + x[2], _dso_xm_y + 40); + _mm_period = _view.get_ruler()->format_real_time(_dso_xm_index[2] - _dso_xm_index[0], sample_rate); + _mm_freq = _view.get_ruler()->format_real_freq(_dso_xm_index[2] - _dso_xm_index[0], sample_rate); + _mm_duty = QString::number((_dso_xm_index[1] - _dso_xm_index[0]) * 100.0 / (_dso_xm_index[2] - _dso_xm_index[0]), 'f', 2)+"%"; + + // -- period show + const QString p_ctr = "P="+_mm_period; + int p_rect_width = p.boundingRect(0, 0, INT_MAX, INT_MAX, + Qt::AlignLeft | Qt::AlignVCenter, p_ctr).width(); + p.drawText(QRect(x[0]+10, _dso_xm_y + 30 - text_height, p_rect_width, text_height), p_ctr); + + // -- frequency show + const QString f_ctr = "F="+_mm_freq; + int f_rect_width = p.boundingRect(0, 0, INT_MAX, INT_MAX, + Qt::AlignLeft | Qt::AlignVCenter, f_ctr).width(); + p.drawText(QRect(x[0]+20 + p_rect_width, _dso_xm_y + 30 - text_height, f_rect_width, text_height), f_ctr); + + // -- duty show + const QString d_ctr = "D="+_mm_duty; + int d_rect_width = p.boundingRect(0, 0, INT_MAX, INT_MAX, + Qt::AlignLeft | Qt::AlignVCenter, d_ctr).width(); + p.drawText(QRect(x[1]+10, _dso_xm_y - 0.5*text_height, d_rect_width, text_height), d_ctr); + + measure_line_count += 3; + } + p.drawLines(measure_lines, measure_line_count); + if (dso_xm_stage < DsoMeasureStages) { + p.drawLine(x[dso_xm_stage-1], _dso_xm_y, + _mouse_point.x(), _dso_xm_y); + p.drawLine(_mouse_point.x(), 0, + _mouse_point.x(), _view.get_viewport()->height()); + } + measure_updated(); + } + + if (_action_type == LOGIC_EDGE) { + p.setPen(QColor(17, 133, 209, 255)); + + p.drawLine(QLineF(_cur_preX, _cur_midY-5, _cur_preX, _cur_midY+5)); + p.drawLine(QLineF(_cur_aftX, _cur_midY-5, _cur_aftX, _cur_midY+5)); + p.drawLine(QLineF(_cur_preX, _cur_midY, _cur_aftX, _cur_midY)); + + int typical_width = p.boundingRect(0, 0, INT_MAX, INT_MAX, + Qt::AlignLeft | Qt::AlignTop, _em_edges).width(); + typical_width = max(typical_width, p.boundingRect(0, 0, INT_MAX, INT_MAX, + Qt::AlignLeft | Qt::AlignTop, _em_rising).width()); + typical_width = max(typical_width, p.boundingRect(0, 0, INT_MAX, INT_MAX, + Qt::AlignLeft | Qt::AlignTop, _em_falling).width()); + + typical_width = typical_width + 30; + + const double width = _view.get_view_width(); + const double height = _view.viewport()->height(); + const double left = _view.hover_point().x(); + const double top = _view.hover_point().y(); + const double right = left + typical_width; + const double bottom = top + 60; + QPointF org_pos = QPointF(right > width ? left - typical_width : left, bottom > height ? top - 80 : top); + QRectF measure_rect = QRectF(org_pos.x(), org_pos.y(), (double)typical_width, 60.0); + QRectF measure1_rect = QRectF(org_pos.x(), org_pos.y(), (double)typical_width, 20.0); + QRectF measure2_rect = QRectF(org_pos.x(), org_pos.y()+20, (double)typical_width, 20.0); + QRectF measure3_rect = QRectF(org_pos.x(), org_pos.y()+40, (double)typical_width, 20.0); + + p.setPen(Qt::NoPen); + p.setBrush(QColor(17, 133, 209, 150)); + p.drawRect(measure_rect); + + p.setPen(Qt::black); + p.drawText(measure1_rect, Qt::AlignRight | Qt::AlignVCenter, _em_edges); + p.drawText(measure2_rect, Qt::AlignRight | Qt::AlignVCenter, _em_rising); + p.drawText(measure3_rect, Qt::AlignRight | Qt::AlignVCenter, _em_falling); + } } diff --git a/DSView/pv/view/viewport.h b/DSView/pv/view/viewport.h index 40a15bbc..17dcf51a 100644 --- a/DSView/pv/view/viewport.h +++ b/DSView/pv/view/viewport.h @@ -54,13 +54,30 @@ public: static const int DsoMeasureStages = 3; static const double MinorDragRateUp; static const double DragDamping; + enum ActionType { + NO_ACTION, + + CURS_MOVE, + + LOGIC_EDGE, + LOGIC_MOVE, + LOGIC_ZOOM, + + DSO_XM_STEP0, + DSO_XM_STEP1, + DSO_XM_STEP2, + DSO_XM_STEP3, + DSO_YM, + DSO_TRIG_MOVE, + + DECODE_REGION + }; + enum MeasureType { NO_MEASURE, LOGIC_FREQ, - LOGIC_EDGE, - LOGIC_MOVE, - LOGIC_CURS, - DSO_FREQ + LOGIC_EDGE_CNT, + DSO_VALUE }; public: @@ -103,7 +120,7 @@ private slots: void set_receive_len(quint64 length); signals: - void mouse_measure(); + void measure_updated(); private: View &_view; @@ -118,11 +135,8 @@ private: QPixmap pixmap; - bool _zoom_rect_visible; - QRectF _zoom_rect; - bool _measure_en; - bool _measure_shown; + ActionType _action_type; MeasureType _measure_type; uint64_t _cur_sample; uint64_t _nxt_sample; @@ -158,13 +172,11 @@ private: QTimer _drag_timer; int _drag_strength; - bool _dso_xm; - int _dso_xm_stage; + bool _dso_xm_valid; int _dso_xm_y; uint64_t _dso_xm_index[DsoMeasureStages]; - bool _dso_ym; - bool _dso_ym_done; + bool _dso_ym_valid; uint16_t _dso_ym_sig_index; double _dso_ym_sig_value; uint64_t _dso_ym_index; From 67ec5fba3a0d98c71da21346603d62e4c4267c2c Mon Sep 17 00:00:00 2001 From: Bryant Mairs Date: Mon, 28 Mar 2016 20:50:45 -0700 Subject: [PATCH 08/32] Correct improper package name for Fedora. --- INSTALL | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/INSTALL b/INSTALL index 1ac01f09..4156b993 100644 --- a/INSTALL +++ b/INSTALL @@ -33,7 +33,7 @@ Step1: Installing the requirements: please check your respective distro's package manager tool if you use other distros Debian/Ubuntu: - $ sudo apt-get install git-core gcc g++ make cmake autoconf automake libtool pkg-config \ + $ sudo apt-get install git-core gcc gcc-c++ make cmake autoconf automake libtool pkg-config \ libglib2.0-dev libzip-dev libudev-dev libusb-1.0-0-dev \ python3-dev qt5-default libboost-dev libboost-test-dev libboost-thread-dev libboost-system-dev libboost-filesystem-dev check From 6b42d57bc78d39606ecb77d3f62edd412e06b8eb Mon Sep 17 00:00:00 2001 From: Bryant Mairs Date: Mon, 28 Mar 2016 20:51:01 -0700 Subject: [PATCH 09/32] Add missing package dependency. --- INSTALL | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/INSTALL b/INSTALL index 4156b993..5c43f660 100644 --- a/INSTALL +++ b/INSTALL @@ -35,7 +35,7 @@ please check your respective distro's package manager tool if you use other dist Debian/Ubuntu: $ sudo apt-get install git-core gcc gcc-c++ make cmake autoconf automake libtool pkg-config \ libglib2.0-dev libzip-dev libudev-dev libusb-1.0-0-dev \ - python3-dev qt5-default libboost-dev libboost-test-dev libboost-thread-dev libboost-system-dev libboost-filesystem-dev check + python3-dev qt5-default libboost-dev libboost-test-dev libboost-thread-dev libboost-system-dev libboost-filesystem-dev check qt5-qtbase-devel Fedora (18, 19): $ sudo yum install git gcc g++ make cmake autoconf automake libtool pkgconfig glib2-devel \ From fbfcb58848fbd95eebcdb10879f50d1319e7604d Mon Sep 17 00:00:00 2001 From: Bryant Mairs Date: Mon, 28 Mar 2016 20:51:27 -0700 Subject: [PATCH 10/32] Add directions for compilation failing on Fedora. --- INSTALL | 3 +++ 1 file changed, 3 insertions(+) diff --git a/INSTALL b/INSTALL index 5c43f660..e3ff4ce0 100644 --- a/INSTALL +++ b/INSTALL @@ -70,6 +70,9 @@ Step3: Building $ cd DSView $ cmake . + + If this step fails, make sure that your pkg-config is properly configured to find the libsigrok and libsigrokdecode libraries (It's not by default in Fedora 23). To do this add "export PKG_CONFIG_PATH="/usr/local/lib/pkgconfig" to your ~/.bashrc and reload it `. ~/.bashrc`. + $ make $ sudo make install From f3138d93c2fc38348bb518f8f1e7c7c854a1b218 Mon Sep 17 00:00:00 2001 From: DreamSourceLab Date: Sun, 10 Apr 2016 08:18:25 +0800 Subject: [PATCH 11/32] Enhance serial trigger @ LA mode --- DSView/pv/dock/triggerdock.cpp | 44 ++++++++++++++++++++++++---------- DSView/pv/dock/triggerdock.h | 2 ++ DSView/pv/view/view.cpp | 10 ++++++++ DSView/pv/view/view.h | 5 ++++ DSView/pv/view/viewport.cpp | 14 +++++++++-- DSView/pv/view/viewport.h | 1 + 6 files changed, 61 insertions(+), 15 deletions(-) diff --git a/DSView/pv/dock/triggerdock.cpp b/DSView/pv/dock/triggerdock.cpp index aea64b19..dc32ad8a 100644 --- a/DSView/pv/dock/triggerdock.cpp +++ b/DSView/pv/dock/triggerdock.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #include "libsigrok4DSL/libsigrok.h" @@ -186,23 +187,32 @@ TriggerDock::TriggerDock(QWidget *parent, SigSession &session) : _serial_value_lineEdit->setInputMask("X X X X X X X X X X X X X X X X"); _serial_value_lineEdit->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); + _serial_vcnt_spinBox = new QSpinBox(_widget); + _serial_vcnt_spinBox->setRange(1, INT32_MAX); + _serial_vcnt_spinBox->setButtonSymbols(QAbstractSpinBox::NoButtons); + QLabel *serial_value_exp_label = new QLabel("1 1 1 1 1 1\n5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0", _widget); serial_value_exp_label->setFont(font); QVBoxLayout *serial_layout = new QVBoxLayout(); QGridLayout *serial_glayout = new QGridLayout(); - serial_glayout->addWidget(serial_value_exp_label, 1, 1, 1, 4); + serial_glayout->addWidget(serial_value_exp_label, 1, 1, 1, 3); serial_glayout->addWidget(_serial_start_label, 2, 0); - serial_glayout->addWidget(_serial_start_lineEdit, 2, 1, 1, 4); - serial_glayout->addWidget(new QLabel(_widget), 2, 2); + serial_glayout->addWidget(_serial_start_lineEdit, 2, 1, 1, 3); + serial_glayout->addWidget(new QLabel(_widget), 2, 4); serial_glayout->addWidget(_serial_stop_label, 3, 0); - serial_glayout->addWidget(_serial_stop_lineEdit, 3, 1, 1, 4); + serial_glayout->addWidget(_serial_stop_lineEdit, 3, 1, 1, 3); serial_glayout->addWidget(_serial_edge_label, 4, 0); - serial_glayout->addWidget(_serial_edge_lineEdit, 4, 1, 1, 4); - serial_glayout->addWidget(_serial_data_lable, 5, 0); - serial_glayout->addWidget(_serial_data_comboBox, 5, 1); - serial_glayout->addWidget(_serial_value_lable, 6, 0); - serial_glayout->addWidget(_serial_value_lineEdit, 6, 1, 1, 4); + serial_glayout->addWidget(_serial_edge_lineEdit, 4, 1, 1, 3); + + serial_glayout->addWidget(new QLabel(_widget), 5, 0, 1, 5); + serial_glayout->addWidget(_serial_data_lable, 6, 0); + serial_glayout->addWidget(_serial_data_comboBox, 6, 1); + serial_glayout->addWidget(new QLabel(tr("counter"), _widget), 6, 4); + serial_glayout->addWidget(_serial_value_lable, 7, 0); + serial_glayout->addWidget(_serial_value_lineEdit, 7, 1, 1, 3); + serial_glayout->addWidget(_serial_vcnt_spinBox, 7, 4); + serial_glayout->addWidget(new QLabel(_widget), 7, 5); serial_layout->addLayout(serial_glayout); serial_layout->addSpacing(20); serial_layout->addWidget(new QLabel(tr("X: Don't care\n0: Low level\n1: High level\nR: Rising edge\nF: Falling edge\nC: Rising/Falling edge"))); @@ -436,10 +446,16 @@ bool TriggerDock::commit_trigger() } // trigger count update - for (i = 0; i < stages_comboBox->currentText().toInt(); i++) { - ds_trigger_stage_set_count(i, TriggerProbes, - _count0_spinBox_list.at(i)->value() - 1, - _count1_spinBox_list.at(i)->value() - 1); + if (_adv_tabWidget->currentIndex() == 0) { + for (i = 0; i < stages_comboBox->currentText().toInt(); i++) { + ds_trigger_stage_set_count(i, TriggerProbes, + _count0_spinBox_list.at(i)->value() - 1, + _count1_spinBox_list.at(i)->value() - 1); + } + } else if(_adv_tabWidget->currentIndex() == 1){ + ds_trigger_stage_set_count(4, TriggerProbes, + _serial_vcnt_spinBox->value() - 1, + 0); } return 1; } @@ -482,6 +498,7 @@ QJsonObject TriggerDock::get_session() trigSes["triggerClock"] = _serial_edge_lineEdit->text(); trigSes["triggerChannel"] = _serial_data_comboBox->currentIndex(); trigSes["triggerData"] = _serial_value_lineEdit->text(); + trigSes["triggerVcnt"] = _serial_vcnt_spinBox->value(); return trigSes; } @@ -518,6 +535,7 @@ void TriggerDock::set_session(QJsonObject ses) _serial_edge_lineEdit->setText(ses["triggerClock"].toString()); _serial_data_comboBox->setCurrentIndex(ses["triggerChannel"].toDouble()); _serial_value_lineEdit->setText(ses["triggerData"].toString()); + _serial_vcnt_spinBox->setValue(ses["triggerVcnt"].toDouble()); } } // namespace dock diff --git a/DSView/pv/dock/triggerdock.h b/DSView/pv/dock/triggerdock.h index 4d9fd4ec..4ec977a3 100644 --- a/DSView/pv/dock/triggerdock.h +++ b/DSView/pv/dock/triggerdock.h @@ -125,6 +125,8 @@ private: QComboBox *_serial_data_comboBox; QLabel *_serial_value_lable; QLineEdit *_serial_value_lineEdit; + QLabel *_serial_vcnt_lable; + QSpinBox *_serial_vcnt_spinBox; }; } // namespace dock diff --git a/DSView/pv/view/view.cpp b/DSView/pv/view/view.cpp index eee4c438..e421a8ce 100644 --- a/DSView/pv/view/view.cpp +++ b/DSView/pv/view/view.cpp @@ -345,6 +345,11 @@ void View::set_trig_pos(quint64 trig_pos) _trig_cursor->set_index(trig_pos); _show_trig_cursor = true; set_scale_offset(_scale, time - _scale * get_view_width() / 2); + + _trigger_time = QDateTime::currentDateTime(); + const int64_t secs = time - _session.get_device()->get_sample_time(); + _trigger_time = _trigger_time.addSecs(secs); + _ruler->update(); _viewport->update(); } @@ -824,5 +829,10 @@ double View::get_max_offset() - _scale * (get_view_width() * MaxViewRate); } +QString View::trigger_time() +{ + return _trigger_time.toString("yyyy-MM-dd hh:mm:ss ddd"); +} + } // namespace view } // namespace pv diff --git a/DSView/pv/view/view.h b/DSView/pv/view/view.h index c3f6ac72..27fabea0 100644 --- a/DSView/pv/view/view.h +++ b/DSView/pv/view/view.h @@ -34,6 +34,7 @@ #include #include +#include #include "../toolbars/samplingbar.h" #include "../data/signaldata.h" @@ -184,6 +185,8 @@ public: void set_sample_limit(uint64_t sample_limit, bool force = false); + QString trigger_time(); + signals: void hover_point_changed(); @@ -272,6 +275,8 @@ private: uint64_t _search_pos; QPointF _hover_point; + + QDateTime _trigger_time; }; } // namespace view diff --git a/DSView/pv/view/viewport.cpp b/DSView/pv/view/viewport.cpp index 2f70b23e..c53e5d51 100644 --- a/DSView/pv/view/viewport.cpp +++ b/DSView/pv/view/viewport.cpp @@ -165,6 +165,8 @@ void Viewport::paintEvent(QPaintEvent *event) if (_view.get_signalHeight() != _curSignalHeight) _curSignalHeight = _view.get_signalHeight(); + paintTrigTime(p); + p.end(); } @@ -813,8 +815,7 @@ void Viewport::measure() } } } - if (_measure_type != NO_MEASURE) - measure_updated(); + measure_updated(); } void Viewport::paintMeasure(QPainter &p) @@ -1169,5 +1170,14 @@ void Viewport::on_drag_timer() } } +void Viewport::paintTrigTime(QPainter &p) +{ + if (_view.session().get_device()->dev_inst()->mode == LOGIC) { + p.setPen(Trace::dsBack); + p.drawText(this->rect(), Qt::AlignRight | Qt::AlignBottom, + "Last Trigger Time: "+_view.trigger_time()); + } +} + } // namespace view } // namespace pv diff --git a/DSView/pv/view/viewport.h b/DSView/pv/view/viewport.h index 17dcf51a..999c72a9 100644 --- a/DSView/pv/view/viewport.h +++ b/DSView/pv/view/viewport.h @@ -110,6 +110,7 @@ private: void paintSignals(QPainter& p); void paintProgress(QPainter& p); void paintMeasure(QPainter &p); + void paintTrigTime(QPainter &p); void measure(); From adf428ffd0ded73d3c7ef4196a7c1a3f4403a2fc Mon Sep 17 00:00:00 2001 From: DreamSourceLab Date: Tue, 3 May 2016 15:47:06 +0800 Subject: [PATCH 12/32] Add protocol list viewer and export support --- DSView/DSView.qrc | 1 + DSView/darkstyle/style.qss | 1218 +++++++++++++++++++++++++ DSView/main.cpp | 3 +- DSView/pv/data/decode/annotation.cpp | 6 + DSView/pv/data/decode/annotation.h | 1 + DSView/pv/data/decode/row.cpp | 4 +- DSView/pv/data/decode/row.h | 2 +- DSView/pv/data/decode/rowdata.cpp | 38 +- DSView/pv/data/decode/rowdata.h | 9 +- DSView/pv/data/decodermodel.cpp | 108 +++ DSView/pv/data/decodermodel.h | 62 ++ DSView/pv/data/decoderstack.cpp | 345 ++++--- DSView/pv/data/decoderstack.h | 35 +- DSView/pv/dialogs/protocolexp.cpp | 215 +++++ DSView/pv/dialogs/protocolexp.h | 85 ++ DSView/pv/dialogs/protocollist.cpp | 183 ++++ DSView/pv/dialogs/protocollist.h | 76 ++ DSView/pv/dock/protocoldock.cpp | 187 +++- DSView/pv/dock/protocoldock.h | 25 +- DSView/pv/mainwindow.cpp | 2 + DSView/pv/sigsession.cpp | 12 +- DSView/pv/sigsession.h | 8 + DSView/pv/toolbars/samplingbar.cpp | 13 +- DSView/pv/view/decodetrace.cpp | 254 +++--- DSView/pv/view/decodetrace.h | 16 +- DSView/pv/view/devmode.cpp | 66 +- DSView/pv/view/devmode.h | 12 +- DSView/pv/view/groupsignal.cpp | 7 +- DSView/pv/view/header.cpp | 4 +- DSView/pv/view/logicsignal.cpp | 66 +- DSView/pv/view/logicsignal.h | 2 +- DSView/pv/view/ruler.cpp | 5 +- DSView/pv/view/ruler.h | 1 + DSView/pv/view/trace.cpp | 98 +- DSView/pv/view/trace.h | 8 +- DSView/pv/view/view.cpp | 22 +- DSView/pv/view/view.h | 1 + DSView/pv/view/viewport.cpp | 10 +- DSView/pv/widgets/decodergroupbox.cpp | 118 ++- DSView/pv/widgets/decodergroupbox.h | 36 +- DSView/stylesheet.qss | 20 + 41 files changed, 2901 insertions(+), 483 deletions(-) create mode 100755 DSView/darkstyle/style.qss create mode 100644 DSView/pv/data/decodermodel.cpp create mode 100644 DSView/pv/data/decodermodel.h create mode 100644 DSView/pv/dialogs/protocolexp.cpp create mode 100644 DSView/pv/dialogs/protocolexp.h create mode 100644 DSView/pv/dialogs/protocollist.cpp create mode 100644 DSView/pv/dialogs/protocollist.h diff --git a/DSView/DSView.qrc b/DSView/DSView.qrc index 05955d6d..76bf2800 100644 --- a/DSView/DSView.qrc +++ b/DSView/DSView.qrc @@ -56,5 +56,6 @@ icons/start_dis.png icons/start_dis_cn.png icons/settings.png + darkstyle/style.qss diff --git a/DSView/darkstyle/style.qss b/DSView/darkstyle/style.qss new file mode 100755 index 00000000..baa433ed --- /dev/null +++ b/DSView/darkstyle/style.qss @@ -0,0 +1,1218 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) <2013-2014> + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +QProgressBar:horizontal { + border: 1px solid #3A3939; + text-align: center; + padding: 1px; + background: #201F1F; +} +QProgressBar::chunk:horizontal { + background-color: qlineargradient(spread:reflect, x1:1, y1:0.545, x2:1, y2:0, stop:0 rgba(28, 66, 111, 255), stop:1 rgba(37, 87, 146, 255)); +} + +QToolTip +{ + border: 1px solid #3A3939; + background-color: rgb(90, 102, 117);; + color: white; + padding: 1px; + opacity: 200; +} + +QWidget +{ + color: silver; + background-color: #302F2F; + selection-background-color:#3d8ec9; + selection-color: black; + background-clip: border; + border-image: none; + outline: 0; +} + +QWidget:item:hover +{ + background-color: #78879b; + color: black; +} + +QWidget:item:selected +{ + background-color: #3d8ec9; +} + +QCheckBox +{ + spacing: 5px; + outline: none; + color: #bbb; + margin-bottom: 2px; +} + +QCheckBox:disabled +{ + color: #777777; +} +QCheckBox::indicator, +QGroupBox::indicator +{ + width: 18px; + height: 18px; +} +QGroupBox::indicator +{ + margin-left: 2px; +} + +QCheckBox::indicator:unchecked, +QCheckBox::indicator:unchecked:hover, +QGroupBox::indicator:unchecked, +QGroupBox::indicator:unchecked:hover +{ + image: url(:/qss_icons/rc/checkbox_unchecked.png); +} + +QCheckBox::indicator:unchecked:focus, +QCheckBox::indicator:unchecked:pressed, +QGroupBox::indicator:unchecked:focus, +QGroupBox::indicator:unchecked:pressed +{ + border: none; + image: url(:/qss_icons/rc/checkbox_unchecked_focus.png); +} + +QCheckBox::indicator:checked, +QCheckBox::indicator:checked:hover, +QGroupBox::indicator:checked, +QGroupBox::indicator:checked:hover +{ + image: url(:/qss_icons/rc/checkbox_checked.png); +} + +QCheckBox::indicator:checked:focus, +QCheckBox::indicator:checked:pressed, +QGroupBox::indicator:checked:focus, +QGroupBox::indicator:checked:pressed +{ + border: none; + image: url(:/qss_icons/rc/checkbox_checked_focus.png); +} + +QCheckBox::indicator:indeterminate, +QCheckBox::indicator:indeterminate:hover, +QCheckBox::indicator:indeterminate:pressed +QGroupBox::indicator:indeterminate, +QGroupBox::indicator:indeterminate:hover, +QGroupBox::indicator:indeterminate:pressed +{ + image: url(:/qss_icons/rc/checkbox_indeterminate.png); +} + +QCheckBox::indicator:indeterminate:focus, +QGroupBox::indicator:indeterminate:focus +{ + image: url(:/qss_icons/rc/checkbox_indeterminate_focus.png); +} + +QCheckBox::indicator:checked:disabled, +QGroupBox::indicator:checked:disabled +{ + image: url(:/qss_icons/rc/checkbox_checked_disabled.png); +} + +QCheckBox::indicator:unchecked:disabled, +QGroupBox::indicator:unchecked:disabled +{ + image: url(:/qss_icons/rc/checkbox_unchecked_disabled.png); +} + +QRadioButton +{ + spacing: 5px; + outline: none; + color: #bbb; + margin-bottom: 2px; +} + +QRadioButton:disabled +{ + color: #777777; +} +QRadioButton::indicator +{ + width: 21px; + height: 21px; +} + +QRadioButton::indicator:unchecked, +QRadioButton::indicator:unchecked:hover +{ + image: url(:/qss_icons/rc/radio_unchecked.png); +} + +QRadioButton::indicator:unchecked:focus, +QRadioButton::indicator:unchecked:pressed +{ + border: none; + outline: none; + image: url(:/qss_icons/rc/radio_unchecked_focus.png); +} + +QRadioButton::indicator:checked, +QRadioButton::indicator:checked:hover +{ + border: none; + outline: none; + image: url(:/qss_icons/rc/radio_checked.png); +} + +QRadioButton::indicator:checked:focus, +QRadioButton::indicato::menu-arrowr:checked:pressed +{ + border: none; + outline: none; + image: url(:/qss_icons/rc/radio_checked_focus.png); +} + +QRadioButton::indicator:indeterminate, +QRadioButton::indicator:indeterminate:hover, +QRadioButton::indicator:indeterminate:pressed +{ + image: url(:/qss_icons/rc/radio_indeterminate.png); +} + +QRadioButton::indicator:checked:disabled +{ + outline: none; + image: url(:/qss_icons/rc/radio_checked_disabled.png); +} + +QRadioButton::indicator:unchecked:disabled +{ + image: url(:/qss_icons/rc/radio_unchecked_disabled.png); +} + + +QMenuBar +{ + background-color: #302F2F; + color: silver; +} + +QMenuBar::item +{ + background: transparent; +} + +QMenuBar::item:selected +{ + background: transparent; + border: 1px solid #3A3939; +} + +QMenuBar::item:pressed +{ + border: 1px solid #3A3939; + background-color: #3d8ec9; + color: black; + margin-bottom:-1px; + padding-bottom:1px; +} + +QMenu +{ + border: 1px solid #3A3939; + color: silver; + margin: 2px; +} + +QMenu::icon +{ + margin: 5px; +} + +QMenu::item +{ + padding: 5px 30px 5px 30px; + margin-left: 5px; + border: 1px solid transparent; /* reserve space for selection border */ +} + +QMenu::item:selected +{ + color: black; +} + +QMenu::separator { + height: 2px; + background: lightblue; + margin-left: 10px; + margin-right: 5px; +} + +QMenu::indicator { + width: 18px; + height: 18px; +} + +/* non-exclusive indicator = check box style indicator + (see QActionGroup::setExclusive) */ +QMenu::indicator:non-exclusive:unchecked { + image: url(:/qss_icons/rc/checkbox_unchecked.png); +} + +QMenu::indicator:non-exclusive:unchecked:selected { + image: url(:/qss_icons/rc/checkbox_unchecked_disabled.png); +} + +QMenu::indicator:non-exclusive:checked { + image: url(:/qss_icons/rc/checkbox_checked.png); +} + +QMenu::indicator:non-exclusive:checked:selected { + image: url(:/qss_icons/rc/checkbox_checked_disabled.png); +} + +/* exclusive indicator = radio button style indicator (see QActionGroup::setExclusive) */ +QMenu::indicator:exclusive:unchecked { + image: url(:/qss_icons/rc/radio_unchecked.png); +} + +QMenu::indicator:exclusive:unchecked:selected { + image: url(:/qss_icons/rc/radio_unchecked_disabled.png); +} + +QMenu::indicator:exclusive:checked { + image: url(:/qss_icons/rc/radio_checked.png); +} + +QMenu::indicator:exclusive:checked:selected { + image: url(:/qss_icons/rc/radio_checked_disabled.png); +} + +QMenu::right-arrow { + margin: 5px; + image: url(:/qss_icons/rc/right_arrow.png) +} + + +QWidget:disabled +{ + color: #404040; + background-color: #302F2F; +} + +QAbstractItemView +{ + alternate-background-color: #3A3939; + color: silver; + border: 1px solid 3A3939; + border-radius: 2px; + padding: 1px; +} + +QTabWidget:focus, QCheckBox:focus, QRadioButton:focus, QSlider:focus +{ + border: none; +} + +QLineEdit +{ + background-color: #201F1F; + padding: 2px; + border-style: solid; + border: 1px solid #3A3939; + border-radius: 2px; + color: silver; +} + +QGroupBox { + border:1px solid #3A3939; + border-radius: 2px; + margin-top: 20px; +} + +QGroupBox::title { + subcontrol-origin: margin; + subcontrol-position: top center; + padding-left: 10px; + padding-right: 10px; + padding-top: 10px; +} + +QScrollBar:horizontal +{ + height: 15px; + margin: 3px 15px 3px 15px; + border: 1px transparent #2A2929; + border-radius: 4px; + background-color: #2A2929; +} + +QScrollBar::handle:horizontal +{ + background-color: #605F5F; + min-width: 5px; + border-radius: 4px; +} + +QScrollBar::add-line:horizontal +{ + margin: 0px 3px 0px 3px; + border-image: url(:/qss_icons/rc/right_arrow_disabled.png); + width: 10px; + height: 10px; + subcontrol-position: right; + subcontrol-origin: margin; +} + +QScrollBar::sub-line:horizontal +{ + margin: 0px 3px 0px 3px; + border-image: url(:/qss_icons/rc/left_arrow_disabled.png); + height: 10px; + width: 10px; + subcontrol-position: left; + subcontrol-origin: margin; +} + +QScrollBar::add-line:horizontal:hover,QScrollBar::add-line:horizontal:on +{ + border-image: url(:/qss_icons/rc/right_arrow.png); + height: 10px; + width: 10px; + subcontrol-position: right; + subcontrol-origin: margin; +} + + +QScrollBar::sub-line:horizontal:hover, QScrollBar::sub-line:horizontal:on +{ + border-image: url(:/qss_icons/rc/left_arrow.png); + height: 10px; + width: 10px; + subcontrol-position: left; + subcontrol-origin: margin; +} + +QScrollBar::up-arrow:horizontal, QScrollBar::down-arrow:horizontal +{ + background: none; +} + + +QScrollBar::add-page:horizontal, QScrollBar::sub-page:horizontal +{ + background: none; +} + +QScrollBar:vertical +{ + background-color: #2A2929; + width: 15px; + margin: 15px 3px 15px 3px; + border: 1px transparent #2A2929; + border-radius: 4px; +} + +QScrollBar::handle:vertical +{ + background-color: #605F5F; + min-height: 5px; + border-radius: 4px; +} + +QScrollBar::sub-line:vertical +{ + margin: 3px 0px 3px 0px; + border-image: url(:/qss_icons/rc/up_arrow_disabled.png); + height: 10px; + width: 10px; + subcontrol-position: top; + subcontrol-origin: margin; +} + +QScrollBar::add-line:vertical +{ + margin: 3px 0px 3px 0px; + border-image: url(:/qss_icons/rc/down_arrow_disabled.png); + height: 10px; + width: 10px; + subcontrol-position: bottom; + subcontrol-origin: margin; +} + +QScrollBar::sub-line:vertical:hover,QScrollBar::sub-line:vertical:on +{ + + border-image: url(:/qss_icons/rc/up_arrow.png); + height: 10px; + width: 10px; + subcontrol-position: top; + subcontrol-origin: margin; +} + + +QScrollBar::add-line:vertical:hover, QScrollBar::add-line:vertical:on +{ + border-image: url(:/qss_icons/rc/down_arrow.png); + height: 10px; + width: 10px; + subcontrol-position: bottom; + subcontrol-origin: margin; +} + +QScrollBar::up-arrow:vertical, QScrollBar::down-arrow:vertical +{ + background: none; +} + + +QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical +{ + background: none; +} + +QTextEdit +{ + background-color: #201F1F; + color: silver; + border: 1px solid #3A3939; +} + +QPlainTextEdit +{ + background-color: #201F1F;; + color: silver; + border-radius: 2px; + border: 1px solid #3A3939; +} + +QHeaderView::section +{ + background-color: #3A3939; + color: silver; + padding-left: 4px; + border: 1px solid #6c6c6c; +} + +QSizeGrip { + image: url(:/qss_icons/rc/sizegrip.png); + width: 12px; + height: 12px; +} + + +QMainWindow::separator +{ + background-color: QLinearGradient( x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0.0 #302F2F, + stop: 0.4 #333333, + stop: 0.5 #404040, + stop: 0.6 #333333, + stop: 1 #302F2F); + color: white; + padding-left: 0px; + spacing: 0px; + width: 3px; + border: 0px solid #202020; +} + +QMenu::separator +{ + height: 1px; + background-color: #3A3939; + color: white; + padding-left: 4px; + margin-left: 10px; + margin-right: 5px; +} + + +QFrame +{ + border-radius: 2px; + border: 1px solid #444; +} + +QFrame[frameShape="0"] +{ + border-radius: 2px; + border: 1px transparent #444; +} + +QStackedWidget +{ + border: 1px transparent black; +} + +QToolBar { + border: 1px transparent #393838; + background: 1px solid #302F2F; + font-weight: bold; +} + +QToolBar::handle:horizontal { + image: url(:/qss_icons/rc/Hmovetoolbar.png); +} +QToolBar::handle:vertical { + image: url(:/qss_icons/rc/Vmovetoolbar.png); +} +QToolBar::separator:horizontal { + image: url(:/qss_icons/rc/Hsepartoolbar.png); +} +QToolBar::separator:vertical { + image: url(:/qss_icons/rc/Vsepartoolbars.png); +} + +QPushButton +{ + color: silver; + background-color: #302F2F; + border-width: 1px; + border-color: #202020; + border-style: solid; + padding-top: 5px; + padding-bottom: 5px; + padding-left: 5px; + padding-right: 5px; + border-radius: 5px; + outline: none; +} + +QPushButton:disabled +{ + background-color: #302F2F; + border-width: 1px; + border-color: #3A3939; + border-style: solid; + padding-top: 5px; + padding-bottom: 5px; + padding-left: 10px; + padding-right: 10px; + border-radius: 5px; + color: #454545; +} + +QComboBox +{ + selection-background-color: #3d8ec9; + background-color: #201F1F; + border-style: solid; + border: 1px solid #3A3939; + border-radius: 2px; + padding: 2px; + min-width: 75px; +} + +QPushButton:checked{ +background-color: QLinearGradient( x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0.0 #302F2F, + stop: 0.5 #6a6868, + stop: 1 #302F2F); +} + +QPushButton:hover +{ +background-color: QLinearGradient( x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0.0 #302F2F, stop: 0.4 #4E4D4D, + stop: 0.5 #6a6868, + stop: 0.6 #4E4D4D, stop: 1 #302F2F); +} + +QComboBox:hover,QAbstractSpinBox:hover,QLineEdit:hover,QTextEdit:hover,QPlainTextEdit:hover,QAbstractView:hover,QTreeView:hover +{ + border: 1px solid #78879b; + color: silver; +} + +QComboBox:on +{ + background-color: #626873; + padding-top: 3px; + padding-left: 4px; + selection-background-color: #4a4a4a; +} + +QComboBox QAbstractItemView +{ + background-color: #201F1F; + border-radius: 2px; + border: 1px solid #444; + selection-background-color: #3d8ec9; +} + +QComboBox::drop-down +{ + subcontrol-origin: padding; + subcontrol-position: top right; + width: 15px; + + border-left-width: 0px; + border-left-color: darkgray; + border-left-style: solid; + border-top-right-radius: 3px; + border-bottom-right-radius: 3px; +} + +QComboBox::down-arrow +{ + image: url(:/qss_icons/rc/down_arrow_disabled.png); +} + +QComboBox::down-arrow:on, QComboBox::down-arrow:hover, +QComboBox::down-arrow:focus +{ + image: url(:/qss_icons/rc/down_arrow.png); +} + +QAbstractSpinBox { + padding-top: 2px; + padding-bottom: 2px; + border: 1px solid #3A3939; + background-color: #201F1F; + color: silver; + border-radius: 2px; + min-width: 75px; +} + +QAbstractSpinBox:up-button +{ + background-color: transparent; + subcontrol-origin: border; + subcontrol-position: center right; +} + +QAbstractSpinBox:down-button +{ + background-color: transparent; + subcontrol-origin: border; + subcontrol-position: center left; +} + +QAbstractSpinBox::up-arrow,QAbstractSpinBox::up-arrow:disabled,QAbstractSpinBox::up-arrow:off { + image: url(:/qss_icons/rc/up_arrow_disabled.png); + width: 10px; + height: 10px; +} +QAbstractSpinBox::up-arrow:hover +{ + image: url(:/qss_icons/rc/up_arrow.png); +} + + +QAbstractSpinBox::down-arrow,QAbstractSpinBox::down-arrow:disabled,QAbstractSpinBox::down-arrow:off +{ + image: url(:/qss_icons/rc/down_arrow_disabled.png); + width: 10px; + height: 10px; +} +QAbstractSpinBox::down-arrow:hover +{ + image: url(:/qss_icons/rc/down_arrow.png); +} + + +QLabel +{ + border: 0px solid black; +} + +QTabWidget{ + border: 1px transparent black; +} + +QTabWidget::pane { + border: 1px transparent #444; + border-radius: 3px; + padding: 3px; +} + +QTabBar +{ + qproperty-drawBase: 0; + left: 5px; /* move to the right by 5px */ +} + +QTabBar:focus +{ + border: 0px transparent black; +} + +QTabBar::close-button { + image: url(:/qss_icons/rc/close.png); + background: transparent; +} + +QTabBar::close-button:hover +{ + image: url(:/qss_icons/rc/close-hover.png); + background: transparent; +} + +QTabBar::close-button:pressed { + image: url(:/qss_icons/rc/close-pressed.png); + background: transparent; +} + +/* TOP TABS */ +QTabBar::tab:top { + color: #b1b1b1; + border: 1px solid #4A4949; + border-bottom: 1px transparent black; + background-color: #302F2F; + padding: 5px; + border-top-left-radius: 2px; + border-top-right-radius: 2px; +} + +QTabBar::tab:top:!selected +{ + color: #b1b1b1; + background-color: #201F1F; + border: 1px transparent #4A4949; + border-bottom: 1px transparent #4A4949; + border-top-left-radius: 0px; + border-top-right-radius: 0px; +} + +QTabBar::tab:top:!selected:hover { + background-color: #48576b; +} + +/* BOTTOM TABS */ +QTabBar::tab:bottom { + color: #b1b1b1; + border: 1px solid #4A4949; + border-top: 1px transparent black; + background-color: #302F2F; + padding: 5px; + border-bottom-left-radius: 2px; + border-bottom-right-radius: 2px; +} + +QTabBar::tab:bottom:!selected +{ + color: #b1b1b1; + background-color: #201F1F; + border: 1px transparent #4A4949; + border-top: 1px transparent #4A4949; + border-bottom-left-radius: 0px; + border-bottom-right-radius: 0px; +} + +QTabBar::tab:bottom:!selected:hover { + background-color: #78879b; +} + +/* LEFT TABS */ +QTabBar::tab:left { + color: #b1b1b1; + border: 1px solid #4A4949; + border-left: 1px transparent black; + background-color: #302F2F; + padding: 5px; + border-top-right-radius: 2px; + border-bottom-right-radius: 2px; +} + +QTabBar::tab:left:!selected +{ + color: #b1b1b1; + background-color: #201F1F; + border: 1px transparent #4A4949; + border-right: 1px transparent #4A4949; + border-top-right-radius: 0px; + border-bottom-right-radius: 0px; +} + +QTabBar::tab:left:!selected:hover { + background-color: #48576b; +} + + +/* RIGHT TABS */ +QTabBar::tab:right { + color: #b1b1b1; + border: 1px solid #4A4949; + border-right: 1px transparent black; + background-color: #302F2F; + padding: 5px; + border-top-left-radius: 2px; + border-bottom-left-radius: 2px; +} + +QTabBar::tab:right:!selected +{ + color: #b1b1b1; + background-color: #201F1F; + border: 1px transparent #4A4949; + border-right: 1px transparent #4A4949; + border-top-left-radius: 0px; + border-bottom-left-radius: 0px; +} + +QTabBar::tab:right:!selected:hover { + background-color: #48576b; +} + +QTabBar QToolButton::right-arrow:enabled { + image: url(:/qss_icons/rc/right_arrow.png); + } + + QTabBar QToolButton::left-arrow:enabled { + image: url(:/qss_icons/rc/left_arrow.png); + } + +QTabBar QToolButton::right-arrow:disabled { + image: url(:/qss_icons/rc/right_arrow_disabled.png); + } + + QTabBar QToolButton::left-arrow:disabled { + image: url(:/qss_icons/rc/left_arrow_disabled.png); + } + + +QDockWidget { + border: 1px transparent #403F3F; + titlebar-close-icon: url(:/qss_icons/rc/close.png); + titlebar-normal-icon: url(:/qss_icons/rc/undock.png); +} + +QDockWidget::close-button, QDockWidget::float-button { + border: 1px solid transparent; + border-radius: 2px; + background: transparent; +} + +QDockWidget::close-button:hover, QDockWidget::float-button:hover { + background: rgba(255, 255, 255, 10); +} + +QDockWidget::close-button:pressed, QDockWidget::float-button:pressed { + padding: 1px -1px -1px 1px; + background: rgba(255, 255, 255, 10); +} + +QTreeView, QListView +{ + border: 1px solid #444; + background-color: #201F1F; +} + +QTreeView:branch:selected, QTreeView:branch:hover +{ + background: url(:/qss_icons/rc/transparent.png); +} + +QTreeView::branch:has-siblings:!adjoins-item { + border-image: url(:/qss_icons/rc/transparent.png); +} + +QTreeView::branch:has-siblings:adjoins-item { + border-image: url(:/qss_icons/rc/transparent.png); +} + +QTreeView::branch:!has-children:!has-siblings:adjoins-item { + border-image: url(:/qss_icons/rc/transparent.png); +} + +QTreeView::branch:has-children:!has-siblings:closed, +QTreeView::branch:closed:has-children:has-siblings { + image: url(:/qss_icons/rc/branch_closed.png); +} + +QTreeView::branch:open:has-children:!has-siblings, +QTreeView::branch:open:has-children:has-siblings { + image: url(:/qss_icons/rc/branch_open.png); +} + +QTreeView::branch:has-children:!has-siblings:closed:hover, +QTreeView::branch:closed:has-children:has-siblings:hover { + image: url(:/qss_icons/rc/branch_closed-on.png); + } + +QTreeView::branch:open:has-children:!has-siblings:hover, +QTreeView::branch:open:has-children:has-siblings:hover { + image: url(:/qss_icons/rc/branch_open-on.png); + } + +QListView::item:!selected:hover, QListView::item:!selected:hover, QTreeView::item:!selected:hover { + background: rgba(0, 0, 0, 0); + outline: 0; + color: #FFFFFF +} + +QListView::item:selected:hover, QListView::item:selected:hover, QTreeView::item:selected:hover { + background: #3d8ec9; + color: #FFFFFF; +} + +QSlider::groove:horizontal { + border: 1px solid #3A3939; + height: 8px; + background: #201F1F; + margin: 2px 0; + border-radius: 2px; +} + +QSlider::handle:horizontal { + background: QLinearGradient( x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0.0 silver, stop: 0.2 #a8a8a8, stop: 1 #727272); + border: 1px solid #3A3939; + width: 14px; + height: 14px; + margin: -4px 0; + border-radius: 2px; +} + +QSlider::groove:vertical { + border: 1px solid #3A3939; + width: 8px; + background: #201F1F; + margin: 0 0px; + border-radius: 2px; +} + +QSlider::handle:vertical { + background: QLinearGradient( x1: 0, y1: 0, x2: 0, y2: 1, stop: 0.0 silver, + stop: 0.2 #a8a8a8, stop: 1 #727272); + border: 1px solid #3A3939; + width: 14px; + height: 14px; + margin: 0 -4px; + border-radius: 2px; +} + +QToolButton { + background-color: transparent; + border: 1px transparent #4A4949; + border-radius: 2px; + margin: 3px; + padding: 3px; +} + +QToolButton[popupMode="1"] { /* only for MenuButtonPopup */ + padding-right: 20px; /* make way for the popup button */ + border: 1px transparent #4A4949; + border-radius: 5px; +} + +QToolButton[popupMode="2"] { /* only for InstantPopup */ + padding-right: 10px; /* make way for the popup button */ + border: 1px transparent #4A4949; +} + + +QToolButton:hover, QToolButton::menu-button:hover { + background-color: QLinearGradient( x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0.0 #302F2F, stop: 0.4 #4E4D4D, + stop: 0.5 #4A4949, + stop: 0.6 #4E4D4D, stop: 1 #302F2F); +} + +QToolButton:checked, QToolButton:pressed, +QToolButton::menu-button:pressed { + background-color: QLinearGradient( x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0.0 #302F2F, + stop: 0.5 #4A4949, + stop: 1.0 #302F2F); +} + +/* the subcontrol below is used only in the InstantPopup or DelayedPopup mode */ +QToolButton::menu-indicator { + image: url(:/qss_icons/rc/down_arrow.png); + top: -7px; left: -2px; /* shift it a bit */ +} + +/* the subcontrols below are used only in the MenuButtonPopup mode */ +QToolButton::menu-button { + border: 1px transparent #4A4949; + border-top-right-radius: 6px; + border-bottom-right-radius: 6px; + /* 16px width + 4px for border = 20px allocated above */ + width: 16px; + outline: none; +} + +QToolButton::menu-arrow { + image: url(:/qss_icons/rc/down_arrow.png); +} + +QToolButton::menu-arrow:open { + top: 1px; left: 1px; /* shift it a bit */ + border: 1px solid #3A3939; +} + +QPushButton::menu-indicator { + subcontrol-origin: padding; + subcontrol-position: bottom right; + left: 8px; +} + +QTableView +{ + border: 1px transparent #444; + gridline-color: #6c6c6c; + background-color: #201F1F; +} + + +QTableView, QHeaderView +{ + border-radius: 0px; +} + +QTableView::item:pressed, QListView::item:pressed, QTreeView::item:pressed { + background: #78879b; + color: #FFFFFF; +} + +QTableView::item:selected:active, QTreeView::item:selected:active, QListView::item:selected:active { + background: #3d8ec9; + color: #FFFFFF; +} + +QHeaderView +{ + border: 1px transparent; + border-radius: 2px; + margin: 0px; + padding: 0px; +} + +QHeaderView::section { + background-color: #302F2F; + color: silver; + padding: 4px; + border: 1px transparent #6c6c6c; + border-radius: 0px; + text-align: center; +} + +QHeaderView::section::vertical::first, QHeaderView::section::vertical::only-one +{ + border-top: 1px transparent #6c6c6c; +} + +QHeaderView::section::vertical +{ + border-top: transparent; +} + +QHeaderView::section::horizontal::first, QHeaderView::section::horizontal::only-one +{ + border-left: 1px transparent #6c6c6c; +} + +QHeaderView::section::horizontal +{ + border-left: transparent; +} + + +QHeaderView::section:checked + { + color: white; + background-color: #5A5959; + } + + /* style the sort indicator */ +QHeaderView::down-arrow { + image: url(:/qss_icons/rc/down_arrow.png); +} + +QHeaderView::up-arrow { + image: url(:/qss_icons/rc/up_arrow.png); +} + + +QTableCornerButton::section { + background-color: #3A3939; + border: 1px solid #3A3939; + border-radius: 2px; +} + +QToolBox { + padding: 3px; + border: 1px transparent black; +} + +QToolBox::tab { + color: #b1b1b1; + background-color: #302F2F; + border: 1px solid #4A4949; + border-bottom: 1px transparent #302F2F; + border-top-left-radius: 5px; + border-top-right-radius: 5px; +} + + QToolBox::tab:selected { /* italicize selected tabs */ + font: italic; + background-color: #302F2F; + border-color: #3d8ec9; + } + +QStatusBar::item { + border: 1px solid #3A3939; + border-radius: 2px; + } + + +QFrame[height="3"], QFrame[width="3"] { + background-color: #444; +} + + +QSplitter::handle { + border: 0px dashed #3A3939; +} + +QSplitter::handle:horizontal { + background-color: QLinearGradient( x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0.0 #302F2F, + stop: 0.4 #333333, + stop: 0.5 #404040, + stop: 0.6 #333333, + stop: 1 #302F2F); + width: 3px; +} + +QSplitter::handle:vertical { + background-color: QLinearGradient( x1: 0, y1: 0, x2: 1, y2: 0, + stop: 0.0 #302F2F, + stop: 0.4 #333333, + stop: 0.5 #404040, + stop: 0.6 #333333, + stop: 1 #302F2F); + height: 3px; +} + +QAbstractScrollArea +{ + border-radius: 2px; + border: 0px transparent #3A3939; + background-color: #302F2F; +} diff --git a/DSView/main.cpp b/DSView/main.cpp index bd131f65..60876c69 100644 --- a/DSView/main.cpp +++ b/DSView/main.cpp @@ -147,7 +147,8 @@ int main(int argc, char *argv[]) // Initialise the main window pv::MainWindow w(device_manager, open_file); - QFile qss(":/stylesheet.qss"); + //QFile qss(":/stylesheet.qss"); + QFile qss(":qdarkstyle/style.qss"); qss.open(QFile::ReadOnly); a.setStyleSheet(qss.readAll()); qss.close(); diff --git a/DSView/pv/data/decode/annotation.cpp b/DSView/pv/data/decode/annotation.cpp index a474dd90..652dc1b9 100644 --- a/DSView/pv/data/decode/annotation.cpp +++ b/DSView/pv/data/decode/annotation.cpp @@ -49,6 +49,12 @@ Annotation::Annotation(const srd_proto_data *const pdata) : } } +Annotation::Annotation() +{ + _start_sample = 0; + _end_sample = 0; +} + Annotation::~Annotation() { _annotations.clear(); diff --git a/DSView/pv/data/decode/annotation.h b/DSView/pv/data/decode/annotation.h index a2676bd3..9ed39bf9 100644 --- a/DSView/pv/data/decode/annotation.h +++ b/DSView/pv/data/decode/annotation.h @@ -35,6 +35,7 @@ class Annotation { public: Annotation(const srd_proto_data *const pdata); + Annotation(); ~Annotation(); uint64_t start_sample() const; diff --git a/DSView/pv/data/decode/row.cpp b/DSView/pv/data/decode/row.cpp index 1c0ee9cc..06ae2987 100644 --- a/DSView/pv/data/decode/row.cpp +++ b/DSView/pv/data/decode/row.cpp @@ -68,8 +68,8 @@ const QString Row::title() const bool Row::operator<(const Row &other) const { - return (_decoder < other._decoder) || - (_decoder == other._decoder && _row < other._row); + return (_decoder->name < other._decoder->name) || + (_decoder->name == other._decoder->name && _row->desc < other._row->desc); } } // decode diff --git a/DSView/pv/data/decode/row.h b/DSView/pv/data/decode/row.h index 6826c0fc..73324302 100644 --- a/DSView/pv/data/decode/row.h +++ b/DSView/pv/data/decode/row.h @@ -46,7 +46,7 @@ public: const QString title() const; - bool operator<(const Row &other) const; + bool operator<(const Row &other) const; private: const srd_decoder *_decoder; diff --git a/DSView/pv/data/decode/rowdata.cpp b/DSView/pv/data/decode/rowdata.cpp index 840459a6..1fbd31a5 100644 --- a/DSView/pv/data/decode/rowdata.cpp +++ b/DSView/pv/data/decode/rowdata.cpp @@ -23,6 +23,7 @@ #include "rowdata.h" using std::max; +using std::min; using std::vector; namespace pv { @@ -30,7 +31,8 @@ namespace data { namespace decode { RowData::RowData() : - _max_annotation(0) + _max_annotation(0), + _min_annotation(UINT64_MAX) { } @@ -51,6 +53,11 @@ uint64_t RowData::get_max_annotation() const return _max_annotation; } +uint64_t RowData::get_min_annotation() const +{ + return _min_annotation; +} + void RowData::get_annotation_subset( vector &dest, uint64_t start_sample, uint64_t end_sample) const @@ -62,10 +69,33 @@ void RowData::get_annotation_subset( dest.push_back(*i); } -void RowData::push_annotation(const Annotation &a) +bool RowData::push_annotation(const Annotation &a) { - _annotations.push_back(a); - _max_annotation = max(_max_annotation, a.end_sample() - a.start_sample()); + try { + _annotations.push_back(a); + _max_annotation = max(_max_annotation, a.end_sample() - a.start_sample()); + if (a.end_sample() != a.start_sample()) + _min_annotation = min(_min_annotation, a.end_sample() - a.start_sample()); + return true; + } catch (const std::bad_alloc&) { + return false; + } +} + +uint64_t RowData::get_annotation_size() const +{ + return _annotations.size(); +} + +bool RowData::get_annotation(Annotation &ann, + uint64_t index) const +{ + if (index < _annotations.size()) { + ann = _annotations[index]; + return true; + } else { + return false; + } } } // decode diff --git a/DSView/pv/data/decode/rowdata.h b/DSView/pv/data/decode/rowdata.h index 03076494..11b88420 100644 --- a/DSView/pv/data/decode/rowdata.h +++ b/DSView/pv/data/decode/rowdata.h @@ -39,6 +39,7 @@ public: uint64_t get_max_sample() const; uint64_t get_max_annotation() const; + uint64_t get_min_annotation() const; /** * Extracts sorted annotations between two period into a vector. */ @@ -46,10 +47,16 @@ public: std::vector &dest, uint64_t start_sample, uint64_t end_sample) const; - void push_annotation(const Annotation &a); + bool push_annotation(const Annotation &a); + + uint64_t get_annotation_size() const; + + bool get_annotation(pv::data::decode::Annotation &ann, + uint64_t index) const; private: uint64_t _max_annotation; + uint64_t _min_annotation; std::vector _annotations; }; diff --git a/DSView/pv/data/decodermodel.cpp b/DSView/pv/data/decodermodel.cpp new file mode 100644 index 00000000..9e2c071f --- /dev/null +++ b/DSView/pv/data/decodermodel.cpp @@ -0,0 +1,108 @@ +/* + * This file is part of the DSView project. + * + * Copyright (C) 2016 Andy Deng + * + * 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 + +#include +#include + +#include +#include + +#include "decoderstack.h" +#include "decodermodel.h" + +using namespace boost; +using namespace std; + +namespace pv { +namespace data { + +DecoderModel::DecoderModel(QObject *parent) + : QAbstractTableModel(parent), + _decoder_stack(NULL) +{ +} + +void DecoderModel::setDecoderStack(boost::shared_ptr decoder_stack) +{ + beginResetModel(); + _decoder_stack = decoder_stack; + endResetModel(); +} + +const boost::shared_ptr& DecoderModel::getDecoderStack() const +{ + return _decoder_stack; +} + +int DecoderModel::rowCount(const QModelIndex & /* parent */) const +{ + if (_decoder_stack) + return _decoder_stack->list_annotation_size(); + else + return 100; +} +int DecoderModel::columnCount(const QModelIndex & /* parent */) const +{ + if (_decoder_stack) + return _decoder_stack->list_rows_size(); + else + return 1; +} + +QVariant DecoderModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) + return QVariant(); + + if (role == Qt::TextAlignmentRole) { + return int(Qt::AlignLeft | Qt::AlignVCenter); + } else if (role == Qt::DisplayRole) { + if (_decoder_stack) { + pv::data::decode::Annotation ann; + if (_decoder_stack->list_annotation(ann, index.column(), index.row())) { + return ann.annotations().at(0); + } + } + } + return QVariant(); +} + +QVariant DecoderModel::headerData(int section, + Qt::Orientation orientation, + int role) const +{ + if (role != Qt::DisplayRole) + return QVariant(); + + if (orientation == Qt::Vertical) + return section; + + if (_decoder_stack) { + QString title; + if (_decoder_stack->list_row_title(section, title)) + return title; + } + return QVariant(); +} + +} // namespace data +} // namespace pv diff --git a/DSView/pv/data/decodermodel.h b/DSView/pv/data/decodermodel.h new file mode 100644 index 00000000..a0eac14b --- /dev/null +++ b/DSView/pv/data/decodermodel.h @@ -0,0 +1,62 @@ +/* + * This file is part of the DSView project. + * + * Copyright (C) 2016 Andy Deng + * + * 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 + */ + +#ifndef DSVIEW_PV_DATA_DECODERMODEL_H +#define DSVIEW_PV_DATA_DECODERMODEL_H + +#include + +#include + +#include + +namespace pv { +namespace data { + +class DecoderStack; + +namespace decode { +class Annotation; +class Decoder; +class Row; +} + +class DecoderModel : public QAbstractTableModel +{ +public: + DecoderModel(QObject *parent = 0); + + int rowCount(const QModelIndex &parent) const; + int columnCount(const QModelIndex &parent) const; + QVariant data(const QModelIndex &index, int role) const; + QVariant headerData(int section, Qt::Orientation orientation, + int role) const; + + void setDecoderStack(boost::shared_ptr decoder_stack); + const boost::shared_ptr& getDecoderStack() const; + +private: + boost::shared_ptr _decoder_stack; +}; + +} // namespace data +} // namespace pv + +#endif // DSVIEW_PV_DATA_DECODERMODEL_H diff --git a/DSView/pv/data/decoderstack.cpp b/DSView/pv/data/decoderstack.cpp index ed720090..e52a91f4 100644 --- a/DSView/pv/data/decoderstack.cpp +++ b/DSView/pv/data/decoderstack.cpp @@ -68,7 +68,8 @@ DecoderStack::DecoderStack(pv::SigSession &session, _frame_complete(false), _samples_decoded(0), _decode_state(Stopped), - _options_changed(false) + _options_changed(false), + _no_memory(false) { connect(&_session, SIGNAL(frame_began()), this, SLOT(on_new_frame())); @@ -79,6 +80,8 @@ DecoderStack::DecoderStack(pv::SigSession &session, _stack.push_back(shared_ptr( new decode::Decoder(dec))); + + build_row(); } DecoderStack::~DecoderStack() @@ -89,7 +92,10 @@ DecoderStack::~DecoderStack() // } stop_decode(); _stack.clear(); - clear(); + _rows.clear(); + _rows_gshow.clear(); + _rows_lshow.clear(); + _class_rows.clear(); } const std::list< boost::shared_ptr >& @@ -102,20 +108,83 @@ void DecoderStack::push(boost::shared_ptr decoder) { assert(decoder); _stack.push_back(decoder); + build_row(); + _options_changed = true; } -void DecoderStack::remove(int index) +void DecoderStack::remove(boost::shared_ptr &decoder) { - assert(index >= 0); - assert(index < (int)_stack.size()); - // Find the decoder in the stack list< shared_ptr >::iterator iter = _stack.begin(); - for(int i = 0; i < index; i++, iter++) - assert(iter != _stack.end()); + for(int i = 0; i < _stack.size(); i++, iter++) + if ((*iter) == decoder) + break; // Delete the element - _stack.erase(iter); + if (iter != _stack.end()) + _stack.erase(iter); + + build_row(); + _options_changed = true; +} + +void DecoderStack::build_row() +{ + _rows.clear(); + // Add classes + BOOST_FOREACH (const shared_ptr &dec, _stack) + { + assert(dec); + const srd_decoder *const decc = dec->decoder(); + assert(dec->decoder()); + + // Add a row for the decoder if it doesn't have a row list + if (!decc->annotation_rows) { + const Row row(decc); + _rows[row] = decode::RowData(); + std::map::const_iterator iter = _rows_gshow.find(row); + if (iter == _rows_gshow.end()) { + if (row.title().contains("bit", Qt::CaseInsensitive) || + row.title().contains("warning", Qt::CaseInsensitive)) { + _rows_gshow[row] = false; + _rows_lshow[row] = false; + } else { + _rows_gshow[row] = true; + _rows_lshow[row] = true; + } + } + } + + // Add the decoder rows + for (const GSList *l = decc->annotation_rows; l; l = l->next) + { + const srd_decoder_annotation_row *const ann_row = + (srd_decoder_annotation_row *)l->data; + assert(ann_row); + + const Row row(decc, ann_row); + + // Add a new empty row data object + _rows[row] = decode::RowData(); + std::map::const_iterator iter = _rows_gshow.find(row); + if (iter == _rows_gshow.end()) { + if (row.title().contains("bit", Qt::CaseInsensitive) || + row.title().contains("warning", Qt::CaseInsensitive)) { + _rows_gshow[row] = false; + _rows_lshow[row] = false; + } else { + _rows_gshow[row] = true; + _rows_lshow[row] = true; + } + } + + // Map out all the classes + for (const GSList *ll = ann_row->ann_classes; + ll; ll = ll->next) + _class_rows[make_pair(decc, + GPOINTER_TO_INT(ll->data))] = row; + } + } } int64_t DecoderStack::samples_decoded() const @@ -124,36 +193,6 @@ int64_t DecoderStack::samples_decoded() const return _samples_decoded; } -std::vector< std::pair > DecoderStack::get_visible_rows() const -{ - lock_guard lock(_output_mutex); - - std::vector< std::pair > rows; - - BOOST_FOREACH (const shared_ptr &dec, _stack) - { - assert(dec); - - const srd_decoder *const decc = dec->decoder(); - assert(dec->decoder()); - - // Add a row for the decoder if it doesn't have a row list - if (!decc->annotation_rows) - rows.push_back(make_pair(Row(decc), dec->shown())); - - // Add the decoder rows - for (const GSList *l = decc->annotation_rows; l; l = l->next) - { - const srd_decoder_annotation_row *const ann_row = - (srd_decoder_annotation_row *)l->data; - assert(ann_row); - rows.push_back(make_pair(Row(decc, ann_row), dec->shown())); - } - } - - return rows; -} - void DecoderStack::get_annotation_subset( std::vector &dest, const Row &row, uint64_t start_sample, @@ -161,9 +200,9 @@ void DecoderStack::get_annotation_subset( { lock_guard lock(_output_mutex); - std::map::const_iterator iter = - _rows.find(row); - if (iter != _rows.end()) + std::map::const_iterator iter = + _rows.find(row); + if (iter != _rows.end()) (*iter).second.get_annotation_subset(dest, start_sample, end_sample); } @@ -180,6 +219,44 @@ uint64_t DecoderStack::get_max_annotation(const Row &row) return 0; } +uint64_t DecoderStack::get_min_annotation(const Row &row) +{ + lock_guard lock(_output_mutex); + + std::map::const_iterator iter = + _rows.find(row); + if (iter != _rows.end()) + return (*iter).second.get_min_annotation(); + + return 0; +} + +std::map& DecoderStack::get_rows_gshow() +{ + return _rows_gshow; +} + +std::map& DecoderStack::get_rows_lshow() +{ + return _rows_lshow; +} + +void DecoderStack::set_rows_gshow(const decode::Row row, bool show) +{ + std::map::const_iterator iter = _rows_gshow.find(row); + if (iter != _rows_gshow.end()) { + _rows_gshow[row] = show; + } +} + +void DecoderStack::set_rows_lshow(const decode::Row row, bool show) +{ + std::map::const_iterator iter = _rows_lshow.find(row); + if (iter != _rows_lshow.end()) { + _rows_lshow[row] = show; + } +} + bool DecoderStack::has_annotations(const Row &row) const { lock_guard lock(_output_mutex); @@ -195,6 +272,55 @@ bool DecoderStack::has_annotations(const Row &row) const return false; } +uint64_t DecoderStack::list_annotation_size() const +{ + uint64_t max_annotation_size = 0; + int row = 0; + for (map::const_iterator i = _rows.begin(); + i != _rows.end(); i++) { + map::const_iterator iter = _rows_lshow.find((*i).first); + if (iter != _rows_lshow.end() && (*iter).second) + max_annotation_size = max(max_annotation_size, + (*i).second.get_annotation_size()); + } + + return max_annotation_size; +} + +bool DecoderStack::list_annotation(pv::data::decode::Annotation &ann, + uint16_t row_index, uint64_t col_index) const +{ + int row = 0; + for (map::const_iterator i = _rows.begin(); + i != _rows.end(); i++) { + map::const_iterator iter = _rows_lshow.find((*i).first); + if (iter != _rows_lshow.end() && (*iter).second) { + if (row_index-- == 0) { + return (*i).second.get_annotation(ann, col_index); + } + } + } + + return false; +} + + +bool DecoderStack::list_row_title(int row, QString &title) const +{ + int index = 0; + for (map::const_iterator i = _rows.begin(); + i != _rows.end(); i++) { + map::const_iterator iter = _rows_lshow.find((*i).first); + if (iter != _rows_lshow.end() && (*iter).second) { + if (row-- == 0) { + title = (*i).first.title(); + return 1; + } + } + } + return 0; +} + QString DecoderStack::error_message() { lock_guard lock(_output_mutex); @@ -207,16 +333,20 @@ void DecoderStack::clear() _frame_complete = false; _samples_decoded = 0; _error_message = QString(); - _rows.clear(); - _class_rows.clear(); + for (map::const_iterator i = _rows.begin(); + i != _rows.end(); i++) + _rows[(*i).first] = decode::RowData(); + _no_memory = false; } void DecoderStack::stop_decode() { _snapshot.reset(); - if(_decode_state == Stopped) + if(_decode_state == Stopped) { + clear(); return; + } if (_decode_thread.get()) { _decode_thread->interrupt(); @@ -224,6 +354,7 @@ void DecoderStack::stop_decode() _decode_state = Stopped; } _decode_thread.reset(); + clear(); } void DecoderStack::begin_decode() @@ -239,8 +370,6 @@ void DecoderStack::begin_decode() // } stop_decode(); - clear(); - // Check that all decoders have the required channels BOOST_FOREACH(const shared_ptr &dec, _stack) if (!dec->have_required_probes()) { @@ -249,37 +378,6 @@ void DecoderStack::begin_decode() return; } - // Add classes - BOOST_FOREACH (const shared_ptr &dec, _stack) - { - assert(dec); - const srd_decoder *const decc = dec->decoder(); - assert(dec->decoder()); - - // Add a row for the decoder if it doesn't have a row list - if (!decc->annotation_rows) - _rows[Row(decc)] = decode::RowData(); - - // Add the decoder rows - for (const GSList *l = decc->annotation_rows; l; l = l->next) - { - const srd_decoder_annotation_row *const ann_row = - (srd_decoder_annotation_row *)l->data; - assert(ann_row); - - const Row row(decc, ann_row); - - // Add a new empty row data object - _rows[row] = decode::RowData(); - - // Map out all the classes - for (const GSList *ll = ann_row->ann_classes; - ll; ll = ll->next) - _class_rows[make_pair(decc, - GPOINTER_TO_INT(ll->data))] = row; - } - } - // We get the logic data of the first channel in the list. // This works because we are currently assuming all // LogicSignals have the same data/snapshot @@ -313,8 +411,8 @@ uint64_t DecoderStack::get_max_sample_count() const { uint64_t max_sample_count = 0; - for (map::const_iterator i = _rows.begin(); - i != _rows.end(); i++) + for (map::const_iterator i = _rows.begin(); + i != _rows.end(); i++) max_sample_count = max(max_sample_count, (*i).second.get_max_sample()); @@ -333,48 +431,6 @@ boost::optional DecoderStack::wait_for_data() const _sample_count); } -//void DecoderStack::decode_data( -// const uint64_t sample_count, const unsigned int unit_size, -// srd_session *const session) -//{ -// //uint8_t chunk[DecodeChunkLength]; -// uint8_t *chunk = NULL; -// //chunk = (uint8_t *)realloc(chunk, DecodeChunkLength); - -// const uint64_t chunk_sample_count = -// DecodeChunkLength / _snapshot->unit_size(); - -// for (uint64_t i = 0; -// !boost::this_thread::interruption_requested() && -// i < sample_count; -// i += chunk_sample_count) -// { -// //lock_guard decode_lock(_global_decode_mutex); - -// const uint64_t chunk_end = min( -// i + chunk_sample_count, sample_count); -// chunk = _snapshot->get_samples(i, chunk_end); - -// if (srd_session_send(session, i, i + sample_count, chunk, -// (chunk_end - i) * unit_size, unit_size) != SRD_OK) { -// _error_message = tr("Decoder reported an error"); -// break; -// } - -// { -// lock_guard lock(_output_mutex); -// _samples_decoded = chunk_end; -// } - -// if (i % DecodeNotifyPeriod == 0) -// new_decode_data(); - -// } -// _options_changed = false; -// decode_done(); -// //new_decode_data(); -//} - void DecoderStack::decode_data( const uint64_t decode_start, const uint64_t decode_end, const unsigned int unit_size, srd_session *const session) @@ -386,7 +442,7 @@ void DecoderStack::decode_data( for (uint64_t i = decode_start; !boost::this_thread::interruption_requested() && - i < decode_end; + i < decode_end && !_no_memory; i += chunk_sample_count) { //lock_guard decode_lock(_global_decode_mutex); @@ -490,6 +546,11 @@ uint64_t DecoderStack::sample_count() const return 0; } +uint64_t DecoderStack::sample_rate() const +{ + return _samplerate; +} + void DecoderStack::annotation_callback(srd_proto_data *pdata, void *decoder) { assert(pdata); @@ -500,6 +561,9 @@ void DecoderStack::annotation_callback(srd_proto_data *pdata, void *decoder) lock_guard lock(d->_output_mutex); + if (d->_no_memory) + return; + const Annotation a(pdata); // Find the row @@ -508,21 +572,21 @@ void DecoderStack::annotation_callback(srd_proto_data *pdata, void *decoder) const srd_decoder *const decc = pdata->pdo->di->decoder; assert(decc); - map::iterator row_iter = d->_rows.end(); + map::iterator row_iter = d->_rows.end(); // Try looking up the sub-row of this class const map, Row>::const_iterator r = d->_class_rows.find(make_pair(decc, a.format())); if (r != d->_class_rows.end()) - row_iter = d->_rows.find((*r).second); + row_iter = d->_rows.find((*r).second); else { // Failing that, use the decoder as a key - row_iter = d->_rows.find(Row(decc)); + row_iter = d->_rows.find(Row(decc)); } - assert(row_iter != d->_rows.end()); - if (row_iter == d->_rows.end()) { + assert(row_iter != d->_rows.end()); + if (row_iter == d->_rows.end()) { qDebug() << "Unexpected annotation: decoder = " << decc << ", format = " << a.format(); assert(0); @@ -530,7 +594,8 @@ void DecoderStack::annotation_callback(srd_proto_data *pdata, void *decoder) } // Add the annotation - (*row_iter).second.push_annotation(a); + if (!(*row_iter).second.push_annotation(a)) + d->_no_memory = true; } void DecoderStack::on_new_frame() @@ -560,21 +625,25 @@ void DecoderStack::on_frame_ended() begin_decode(); } -int DecoderStack::cur_rows_size() +int DecoderStack::list_rows_size() { int rows_size = 0; + int row = 0; for (map::const_iterator i = _rows.begin(); - i != _rows.end(); i++) - if ((*i).second.get_max_sample() != 0) + i != _rows.end(); i++) { + map::const_iterator iter = _rows_lshow.find((*i).first); + if (iter != _rows_lshow.end() && (*iter).second) rows_size++; - - if (rows_size == 0) - return 1; - else - return rows_size; + } + return rows_size; } -void DecoderStack::options_changed(bool changed) +bool DecoderStack::options_changed() const +{ + return _options_changed; +} + +void DecoderStack::set_options_changed(bool changed) { _options_changed = changed; } diff --git a/DSView/pv/data/decoderstack.h b/DSView/pv/data/decoderstack.h index 0e71dd52..a60d85dc 100644 --- a/DSView/pv/data/decoderstack.h +++ b/DSView/pv/data/decoderstack.h @@ -86,14 +86,13 @@ public: virtual ~DecoderStack(); - const std::list< boost::shared_ptr >& stack() const; + const std::list< boost::shared_ptr >& stack() const; void push(boost::shared_ptr decoder); - void remove(int index); + void remove(boost::shared_ptr& decoder); + void build_row(); int64_t samples_decoded() const; - std::vector< std::pair > get_visible_rows() const; - /** * Extracts sorted annotations between two period into a vector. */ @@ -103,9 +102,23 @@ public: uint64_t end_sample) const; uint64_t get_max_annotation(const decode::Row &row); + uint64_t get_min_annotation(const decode::Row &row); // except instant(end=start) annotation + + std::map &get_rows_gshow(); + std::map &get_rows_lshow(); + void set_rows_gshow(const decode::Row row, bool show); + void set_rows_lshow(const decode::Row row, bool show); bool has_annotations(const decode::Row &row) const; + uint64_t list_annotation_size() const; + + bool list_annotation(decode::Annotation &ann, + uint16_t row_index, uint64_t col_index) const; + + + bool list_row_title(int row, QString &title) const; + QString error_message(); void clear(); @@ -116,11 +129,13 @@ public: void stop_decode(); - int cur_rows_size(); + int list_rows_size(); - void options_changed(bool changed); + bool options_changed() const; + void set_options_changed(bool changed); uint64_t sample_count() const; + uint64_t sample_rate() const; private: boost::optional wait_for_data() const; @@ -170,9 +185,10 @@ private: mutable boost::mutex _output_mutex; int64_t _samples_decoded; - std::map _rows; - - std::map, decode::Row> _class_rows; + std::map _rows; + std::map _rows_gshow; + std::map _rows_lshow; + std::map, decode::Row> _class_rows; QString _error_message; @@ -180,6 +196,7 @@ private: decode_state _decode_state; bool _options_changed; + bool _no_memory; friend class DecoderStackTest::TwoDecoderStack; }; diff --git a/DSView/pv/dialogs/protocolexp.cpp b/DSView/pv/dialogs/protocolexp.cpp new file mode 100644 index 00000000..c3baf6bb --- /dev/null +++ b/DSView/pv/dialogs/protocolexp.cpp @@ -0,0 +1,215 @@ +/* + * This file is part of the DSView project. + * DSView is based on PulseView. + * + * Copyright (C) 2012 Joel Holdsworth + * Copyright (C) 2013 DreamSourceLab + * + * 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 "protocolexp.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../sigsession.h" +#include "../data/decoderstack.h" +#include "../data/decode/row.h" +#include "../data/decode/annotation.h" +#include "../view/decodetrace.h" +#include "../data/decodermodel.h" + +using namespace boost; +using namespace std; + +namespace pv { +namespace dialogs { + +ProtocolExp::ProtocolExp(QWidget *parent, SigSession &session) : + QDialog(parent), + _session(session), + _button_box(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, + Qt::Horizontal, this), + _export_cancel(false) +{ + _format_combobox = new QComboBox(this); + _format_combobox->addItem(tr("Comma-Separated Values (*.csv)")); + _format_combobox->addItem(tr("Text files (*.txt)")); + + _flayout = new QFormLayout(); + _flayout->addRow(new QLabel(tr("Export Format: "), this), _format_combobox); + + pv::data::DecoderModel* decoder_model = _session.get_decoder_model(); + const boost::shared_ptr& decoder_stack = decoder_model->getDecoderStack(); + if (decoder_stack) { + int row_index = 0; + const std::map& rows(decoder_stack->get_rows_lshow()); + for (std::map::const_iterator i = rows.begin(); + i != rows.end(); i++) { + if ((*i).second) { + QLabel *row_label = new QLabel((*i).first.title(), this); + QRadioButton *row_sel = new QRadioButton(this); + if (row_index == 0) { + row_sel->setChecked(true); + } + _row_label_list.push_back(row_label); + _row_sel_list.push_back(row_sel); + _flayout->addRow(row_label, row_sel); + row_sel->setProperty("index", row_index); + row_sel->setProperty("title", (*i).first.title()); + row_index++; + } + } + } + + _layout = new QVBoxLayout(this); + _layout->addLayout(_flayout); + _layout->addWidget(&_button_box); + setLayout(_layout); + + connect(&_button_box, SIGNAL(accepted()), this, SLOT(accept())); + connect(&_button_box, SIGNAL(rejected()), this, SLOT(reject())); +} + +void ProtocolExp::accept() +{ + using namespace Qt; + using namespace pv::data::decode; + + QDialog::accept(); + + if (!_row_sel_list.empty()) { + QList supportedFormats; + for (int i = _format_combobox->count() - 1; i >= 0; i--) { + supportedFormats.push_back(_format_combobox->itemText(i)); + } + QString filter; + for(int i = 0; i < supportedFormats.count();i++){ + filter.append(supportedFormats[i]); + if(i < supportedFormats.count() - 1) + filter.append(";;"); + } + QString default_filter = _format_combobox->currentText(); + QString file_name = QFileDialog::getSaveFileName( + this, tr("Export Data"), "",filter,&default_filter); + if (!file_name.isEmpty()) { + QFileInfo f(file_name); + QStringList list = default_filter.split('.').last().split(')'); + QString ext = list.first(); + if(f.suffix().compare(ext)) + file_name+=tr(".")+ext; + + QFile file(file_name); + file.open(QIODevice::WriteOnly | QIODevice::Text); + QTextStream out(&file); + out.setCodec("UTF-8"); + out.setGenerateByteOrderMark(true); + + QFuture future; + future = QtConcurrent::run([&]{ + _export_cancel = false; + QString title; + int index; + for (std::list::const_iterator i = _row_sel_list.begin(); + i != _row_sel_list.end(); i++) { + if ((*i)->isChecked()) { + title = (*i)->property("title").toString(); + index = (*i)->property("index").toULongLong(); + break; + } + } + out << QString("%1;%2;%3\n") + .arg("ID") + .arg("Time[s]") + .arg(title); + + pv::data::DecoderModel* decoder_model = _session.get_decoder_model(); + const boost::shared_ptr& decoder_stack = decoder_model->getDecoderStack(); + int row_index = 0; + Row row; + const std::map& rows_lshow(decoder_stack->get_rows_lshow()); + for (std::map::const_iterator i = rows_lshow.begin(); + i != rows_lshow.end(); i++) { + if ((*i).second) { + if (index == row_index) { + row = (*i).first; + break; + } + row_index++; + } + } + + uint64_t exported = 0; + double time_pre_samples = 1.0 / decoder_stack->samplerate(); + vector annotations; + decoder_stack->get_annotation_subset(annotations, row, + 0, decoder_stack->sample_count()-1); + if (!annotations.empty()) { + BOOST_FOREACH(const Annotation &a, annotations) { + out << QString("%1;%2;%3\n") + .arg(QString::number(exported)) + .arg(QString::number(a.start_sample()*time_pre_samples)) + .arg(a.annotations().at(0)); + exported++; + emit export_progress(exported*100/annotations.size()); + if (_export_cancel) + break; + } + } + }); + Qt::WindowFlags flags = Qt::CustomizeWindowHint; + QProgressDialog dlg(tr("Export Protocol List Result... It can take a while."), + tr("Cancel"),0,100,this,flags); + dlg.setWindowModality(Qt::WindowModal); + dlg.setWindowFlags(Qt::Dialog | Qt::FramelessWindowHint); + + QFutureWatcher watcher; + connect(&watcher,SIGNAL(finished()),&dlg,SLOT(cancel())); + connect(this,SIGNAL(export_progress(int)),&dlg,SLOT(setValue(int))); + connect(&dlg,SIGNAL(canceled()),this,SLOT(cancel_export())); + watcher.setFuture(future); + dlg.exec(); + + future.waitForFinished(); + file.close(); + } + } +} + +void ProtocolExp::reject() +{ + using namespace Qt; + + QDialog::reject(); +} + +void ProtocolExp::cancel_export() +{ + _export_cancel = true; +} + +} // namespace dialogs +} // namespace pv diff --git a/DSView/pv/dialogs/protocolexp.h b/DSView/pv/dialogs/protocolexp.h new file mode 100644 index 00000000..c793a75c --- /dev/null +++ b/DSView/pv/dialogs/protocolexp.h @@ -0,0 +1,85 @@ +/* + * This file is part of the DSView project. + * DSView is based on PulseView. + * + * Copyright (C) 2012 Joel Holdsworth + * Copyright (C) 2013 DreamSourceLab + * + * 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 + */ + + +#ifndef DSVIEW_PV_PROTOCOLEXP_H +#define DSVIEW_PV_PROTOCOLEXP_H + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "../device/devinst.h" +#include "../prop/binding/deviceoptions.h" + +namespace pv { + +class SigSession; + +namespace data { +namespace decode { +class Row; +} +} + +namespace dialogs { + +class ProtocolExp : public QDialog +{ + Q_OBJECT + +public: + ProtocolExp(QWidget *parent, SigSession &session); + +protected: + void accept(); + void reject(); + +signals: + void export_progress(int percent); + +private slots: + void cancel_export(); + +private: + SigSession &_session; + + QComboBox *_format_combobox; + std::list _row_sel_list; + std::list _row_label_list; + QFormLayout *_flayout; + QVBoxLayout *_layout; + QDialogButtonBox _button_box; + + bool _export_cancel; +}; + +} // namespace dialogs +} // namespace pv + +#endif // DSVIEW_PV_PROTOCOLEXP_H diff --git a/DSView/pv/dialogs/protocollist.cpp b/DSView/pv/dialogs/protocollist.cpp new file mode 100644 index 00000000..6f6e090c --- /dev/null +++ b/DSView/pv/dialogs/protocollist.cpp @@ -0,0 +1,183 @@ +/* + * This file is part of the DSView project. + * DSView is based on PulseView. + * + * Copyright (C) 2012 Joel Holdsworth + * Copyright (C) 2013 DreamSourceLab + * + * 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 "protocollist.h" + +#include + +#include +#include +#include + +#include "../sigsession.h" +#include "../data/decoderstack.h" +#include "../data/decode/row.h" +#include "../view/decodetrace.h" +#include "../data/decodermodel.h" + +using namespace boost; +using namespace std; + +namespace pv { +namespace dialogs { + +ProtocolList::ProtocolList(QWidget *parent, SigSession &session) : + QDialog(parent), + _session(session), + _button_box(QDialogButtonBox::Ok, + Qt::Horizontal, this) +{ + pv::data::DecoderModel* decoder_model = _session.get_decoder_model(); + + + _protocol_combobox = new QComboBox(this); + const std::vector< boost::shared_ptr > decode_sigs( + _session.get_decode_signals()); + int index = 0; + BOOST_FOREACH(boost::shared_ptr d, decode_sigs) { + _protocol_combobox->addItem(d->get_name()); + if (decoder_model->getDecoderStack() == d->decoder()) + _protocol_combobox->setCurrentIndex(index); + index++; + } + _protocol_combobox->addItem("", qVariantFromValue(NULL)); + if (decoder_model->getDecoderStack() == NULL) + _protocol_combobox->setCurrentIndex(index); + + _flayout = new QFormLayout(); + _flayout->addRow(new QLabel(tr("Decoded Protocols: "), this), _protocol_combobox); + + _layout = new QVBoxLayout(this); + _layout->addLayout(_flayout); + _layout->addWidget(&_button_box); + setLayout(_layout); + + connect(&_button_box, SIGNAL(accepted()), this, SLOT(accept())); + connect(_protocol_combobox, SIGNAL(currentIndexChanged(int)), this, SLOT(set_protocol(int))); + set_protocol(_protocol_combobox->currentIndex()); +} + +void ProtocolList::accept() +{ + using namespace Qt; + + QDialog::accept(); +} + +void ProtocolList::reject() +{ + using namespace Qt; + + QDialog::accept(); +} + +void ProtocolList::set_protocol(int index) +{ + (void)index; + + for(std::list::const_iterator i = _show_checkbox_list.begin(); + i != _show_checkbox_list.end(); i++) { + (*i)->setParent(NULL); + _flayout->removeWidget((*i)); + delete (*i); + } + _show_checkbox_list.clear(); + for(std::list::const_iterator i = _show_label_list.begin(); + i != _show_label_list.end(); i++) { + (*i)->setParent(NULL); + _flayout->removeWidget((*i)); + delete (*i); + } + _show_label_list.clear(); + + boost::shared_ptr decoder_stack; + const std::vector< boost::shared_ptr > decode_sigs( + _session.get_decode_signals()); + int cur_index = 0; + BOOST_FOREACH(boost::shared_ptr d, decode_sigs) { + if (index == cur_index) { + decoder_stack = d->decoder(); + break; + } + cur_index++; + } + + if (!decoder_stack){ + _session.get_decoder_model()->setDecoderStack(NULL); + return; + } + + _session.get_decoder_model()->setDecoderStack(decoder_stack); + int row_index = 0; + const std::map& rows(decoder_stack->get_rows_lshow()); + for (std::map::const_iterator i = rows.begin(); + i != rows.end(); i++) { + QLabel *row_label = new QLabel((*i).first.title(), this); + QCheckBox *row_checkbox = new QCheckBox(this); + //row_checkbox->setChecked(false); + _show_label_list.push_back(row_label); + _show_checkbox_list.push_back(row_checkbox); + _flayout->addRow(row_label, row_checkbox); + + row_checkbox->setChecked((*i).second); + connect(row_checkbox, SIGNAL(clicked(bool)), this, SLOT(on_row_check(bool))); + row_checkbox->setProperty("index", row_index); + row_index++; + } +} + +void ProtocolList::on_row_check(bool show) +{ + QCheckBox *sc = dynamic_cast(sender()); + QVariant id = sc->property("index"); + int index = id.toInt(); + + boost::shared_ptr decoder_stack; + const std::vector< boost::shared_ptr > decode_sigs( + _session.get_decode_signals()); + int cur_index = 0; + BOOST_FOREACH(boost::shared_ptr d, decode_sigs) { + if (cur_index == _protocol_combobox->currentIndex()) { + decoder_stack = d->decoder(); + break; + } + cur_index++; + } + + if (!decoder_stack) + return; + + std::map& rows(decoder_stack->get_rows_lshow()); + for (std::map::const_iterator i = rows.begin(); + i != rows.end(); i++) { + if (index-- == 0) { + decoder_stack->set_rows_lshow((*i).first, show); + break; + } + } + + _session.get_decoder_model()->setDecoderStack(decoder_stack); +} + +} // namespace dialogs +} // namespace pv diff --git a/DSView/pv/dialogs/protocollist.h b/DSView/pv/dialogs/protocollist.h new file mode 100644 index 00000000..6d315322 --- /dev/null +++ b/DSView/pv/dialogs/protocollist.h @@ -0,0 +1,76 @@ +/* + * This file is part of the DSView project. + * DSView is based on PulseView. + * + * Copyright (C) 2012 Joel Holdsworth + * Copyright (C) 2013 DreamSourceLab + * + * 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 + */ + + +#ifndef DSVIEW_PV_PROTOCOLLIST_H +#define DSVIEW_PV_PROTOCOLLIST_H + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "../device/devinst.h" +#include "../prop/binding/deviceoptions.h" + +namespace pv { + +class SigSession; + +namespace dialogs { + +class ProtocolList : public QDialog +{ + Q_OBJECT + +public: + ProtocolList(QWidget *parent, SigSession &session); + +protected: + void accept(); + void reject(); + +private slots: + void set_protocol(int index); + void on_row_check(bool show); + +private: + SigSession &_session; + + QComboBox *_protocol_combobox; + std::list _show_checkbox_list; + std::list _show_label_list; + QFormLayout *_flayout; + QVBoxLayout *_layout; + QDialogButtonBox _button_box; + +}; + +} // namespace dialogs +} // namespace pv + +#endif // DSVIEW_PV_PROTOCOLLIST_H diff --git a/DSView/pv/dock/protocoldock.cpp b/DSView/pv/dock/protocoldock.cpp index 7756af9e..637022e1 100644 --- a/DSView/pv/dock/protocoldock.cpp +++ b/DSView/pv/dock/protocoldock.cpp @@ -25,13 +25,21 @@ #include "../sigsession.h" #include "../view/decodetrace.h" #include "../device/devinst.h" +#include "../data/decodermodel.h" +#include "../data/decoderstack.h" +#include "../dialogs/protocollist.h" +#include "../dialogs/protocolexp.h" #include #include #include #include #include +#include +#include +#include +#include #include namespace pv { @@ -41,20 +49,20 @@ ProtocolDock::ProtocolDock(QWidget *parent, SigSession &session) : QScrollArea(parent), _session(session) { - _widget = new QWidget(this); + _up_widget = new QWidget(this); QHBoxLayout *hori_layout = new QHBoxLayout(); - _add_button = new QPushButton(_widget); + _add_button = new QPushButton(_up_widget); _add_button->setFlat(true); _add_button->setIcon(QIcon::fromTheme("protocol", QIcon(":/icons/add.png"))); - _del_all_button = new QPushButton(_widget); + _del_all_button = new QPushButton(_up_widget); _del_all_button->setFlat(true); _del_all_button->setIcon(QIcon::fromTheme("protocol", QIcon(":/icons/del.png"))); _del_all_button->setCheckable(true); - _protocol_combobox = new QComboBox(_widget); + _protocol_combobox = new QComboBox(_up_widget); GSList *l = g_slist_sort(g_slist_copy( (GSList*)srd_decoder_list()), decoder_name_cmp); @@ -80,15 +88,72 @@ ProtocolDock::ProtocolDock(QWidget *parent, SigSession &session) : connect(_del_all_button, SIGNAL(clicked()), this, SLOT(del_protocol())); - _layout = new QVBoxLayout(); - _layout->addLayout(hori_layout); - _layout->addStretch(1); + _up_layout = new QVBoxLayout(); + _up_layout->addLayout(hori_layout); + _up_layout->addStretch(1); - _widget->setLayout(_layout); + _up_widget->setLayout(_up_layout); + _up_widget->setMinimumHeight(120); - this->setWidget(_widget); - _widget->setGeometry(0, 0, sizeHint().width(), 500); - _widget->setObjectName("protocolWidget"); +// this->setWidget(_widget); +// _widget->setGeometry(0, 0, sizeHint().width(), 500); +// _widget->setObjectName("protocolWidget"); + + _dn_widget = new QWidget(this); + + _dn_set_button = new QPushButton(_dn_widget); + _dn_set_button->setFlat(true); + _dn_set_button->setIcon(QIcon::fromTheme("protocol", + QIcon(":/icons/gear.png"))); + connect(_dn_set_button, SIGNAL(clicked()), + this, SLOT(set_model())); + + _dn_save_button = new QPushButton(_dn_widget); + _dn_save_button->setFlat(true); + _dn_save_button->setIcon(QIcon::fromTheme("protocol", + QIcon(":/icons/save.png"))); + connect(_dn_save_button, SIGNAL(clicked()), + this, SLOT(export_table_view())); + + QHBoxLayout *dn_title_layout = new QHBoxLayout(); + dn_title_layout->addWidget(_dn_set_button, 0, Qt::AlignLeft); + dn_title_layout->addWidget(_dn_save_button, 0, Qt::AlignLeft); + dn_title_layout->addWidget(new QLabel(tr("Protocol List Viewer"), _dn_widget), 1, Qt::AlignLeft); + dn_title_layout->addStretch(1); + + _table_view = new QTableView(_dn_widget); + _table_view->setModel(_session.get_decoder_model()); + _table_view->setAlternatingRowColors(true); + _table_view->setShowGrid(false); + _table_view->horizontalHeader()->setStretchLastSection(true); + _table_view->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel); + _table_view->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); + + QVBoxLayout *dn_layout = new QVBoxLayout(); + dn_layout->addLayout(dn_title_layout); + dn_layout->addWidget(_table_view); + + _dn_widget->setLayout(dn_layout); + _dn_widget->setMinimumHeight(400); + + _split_widget = new QSplitter(this); + _split_widget->insertWidget(0, _up_widget); + _split_widget->insertWidget(1, _dn_widget); + _split_widget->setOrientation(Qt::Vertical); + _split_widget->setCollapsible(0, false); + _split_widget->setCollapsible(1, false); + //_split_widget->setStretchFactor(1, 1); + //_split_widget + + this->setWidgetResizable(true); + this->setWidget(_split_widget); + //_split_widget->setGeometry(0, 0, sizeHint().width(), 500); + _split_widget->setObjectName("protocolWidget"); + + connect(&_session, SIGNAL(decode_done()), this, SLOT(update_model())); + connect(this, SIGNAL(protocol_updated()), this, SLOT(update_model())); + connect(_table_view, SIGNAL(clicked(QModelIndex)), this, SLOT(item_clicked(QModelIndex))); + connect(_table_view->horizontalHeader(), SIGNAL(sectionResized(int,int,int)), this, SLOT(column_resize(int, int, int))); } ProtocolDock::~ProtocolDock() @@ -126,15 +191,15 @@ void ProtocolDock::add_protocol() //QMap & _options = dlg.get_options(); //QMap _options_index = dlg.get_options_index(); - QPushButton *_del_button = new QPushButton(_widget); - QPushButton *_set_button = new QPushButton(_widget); + QPushButton *_del_button = new QPushButton(_up_widget); + QPushButton *_set_button = new QPushButton(_up_widget); _del_button->setFlat(true); _del_button->setIcon(QIcon::fromTheme("protocol", QIcon(":/icons/del.png"))); _set_button->setFlat(true); _set_button->setIcon(QIcon::fromTheme("protocol", QIcon(":/icons/gear.png"))); - QLabel *_protocol_label = new QLabel(_widget); + QLabel *_protocol_label = new QLabel(_up_widget); _del_button->setCheckable(true); _protocol_label->setText(_protocol_combobox->currentText()); @@ -155,9 +220,10 @@ void ProtocolDock::add_protocol() hori_layout->addWidget(_protocol_label); hori_layout->addStretch(1); _hori_layout_list.push_back(hori_layout); - _layout->insertLayout(_del_button_list.size(), hori_layout); + _up_layout->insertLayout(_del_button_list.size(), hori_layout); //_session.add_protocol_analyzer(_protocol_combobox->currentIndex(), _sel_probes, _options, _options_index); + protocol_updated(); } } } @@ -183,6 +249,7 @@ void ProtocolDock::rst_protocol() } rst_index++; } + protocol_updated(); } void ProtocolDock::del_protocol() @@ -193,7 +260,7 @@ void ProtocolDock::del_protocol() int del_index = 0; for (QVector ::const_iterator i = _hori_layout_list.begin(); i != _hori_layout_list.end(); i++) { - _layout->removeItem((*i)); + _up_layout->removeItem((*i)); delete (*i); delete _del_button_list.at(del_index); delete _set_button_list.at(del_index); @@ -220,7 +287,7 @@ void ProtocolDock::del_protocol() for (QVector ::const_iterator i = _del_button_list.begin(); i != _del_button_list.end(); i++) { if ((*i)->isChecked()) { - _layout->removeItem(_hori_layout_list.at(del_index)); + _up_layout->removeItem(_hori_layout_list.at(del_index)); delete _hori_layout_list.at(del_index); delete _del_button_list.at(del_index); @@ -234,12 +301,12 @@ void ProtocolDock::del_protocol() _protocol_index_list.remove(del_index); _session.remove_decode_signal(del_index); - break; } del_index++; } } + protocol_updated(); } void ProtocolDock::del_all_protocol() @@ -248,7 +315,7 @@ void ProtocolDock::del_all_protocol() int del_index = 0; for (QVector ::const_iterator i = _hori_layout_list.begin(); i != _hori_layout_list.end(); i++) { - _layout->removeItem((*i)); + _up_layout->removeItem((*i)); delete (*i); delete _del_button_list.at(del_index); delete _set_button_list.at(del_index); @@ -262,8 +329,90 @@ void ProtocolDock::del_all_protocol() _set_button_list.clear(); _protocol_label_list.clear(); _protocol_index_list.clear(); + + protocol_updated(); } } +void ProtocolDock::set_model() +{ + pv::dialogs::ProtocolList *protocollist_dlg = new pv::dialogs::ProtocolList(this, _session); + protocollist_dlg->exec(); + resize_table_view(_session.get_decoder_model()); +} + +void ProtocolDock::update_model() +{ + pv::data::DecoderModel *decoder_model = _session.get_decoder_model(); + const std::vector< boost::shared_ptr > decode_sigs( + _session.get_decode_signals()); + if (decode_sigs.size() == 0) + decoder_model->setDecoderStack(NULL); + else if (!decoder_model->getDecoderStack()) + decoder_model->setDecoderStack(decode_sigs.at(0)->decoder()); + else { + int index = 0; + BOOST_FOREACH(const boost::shared_ptr d, decode_sigs) { + if (d->decoder() == decoder_model->getDecoderStack()) { + decoder_model->setDecoderStack(d->decoder()); + break; + } + index++; + } + if (index >= decode_sigs.size()) + decoder_model->setDecoderStack(decode_sigs.at(0)->decoder()); + } + + resize_table_view(decoder_model); +} + +void ProtocolDock::resize_table_view(data::DecoderModel* decoder_model) +{ + if (decoder_model->getDecoderStack()) { + for (int i = 0; i < decoder_model->columnCount(QModelIndex()) - 1; i++) { + _table_view->resizeColumnToContents(i); + if (_table_view->columnWidth(i) > 200) + _table_view->setColumnWidth(i, 200); + } + int top_row = _table_view->rowAt(0); + int bom_row = _table_view->rowAt(_table_view->height()); + if (bom_row >= top_row && top_row >= 0) { + for (int i = top_row; i <= bom_row; i++) + _table_view->resizeRowToContents(i); + } + } +} + +void ProtocolDock::item_clicked(const QModelIndex &index) +{ + pv::data::DecoderModel *decoder_model = _session.get_decoder_model(); + boost::shared_ptr decoder_stack = decoder_model->getDecoderStack(); + if (decoder_stack) { + pv::data::decode::Annotation ann; + if (decoder_stack->list_annotation(ann, index.column(), index.row())) { + _session.show_region(ann.start_sample(), ann.end_sample()); + } + } +} + +void ProtocolDock::column_resize(int index, int old_size, int new_size) +{ + pv::data::DecoderModel *decoder_model = _session.get_decoder_model(); + if (decoder_model->getDecoderStack()) { + int top_row = _table_view->rowAt(0); + int bom_row = _table_view->rowAt(_table_view->height()); + if (bom_row >= top_row && top_row >= 0) { + for (int i = top_row; i <= bom_row; i++) + _table_view->resizeRowToContents(i); + } + } +} + +void ProtocolDock::export_table_view() +{ + pv::dialogs::ProtocolExp *protocolexp_dlg = new pv::dialogs::ProtocolExp(this, _session); + protocolexp_dlg->exec(); +} + } // namespace dock } // namespace pv diff --git a/DSView/pv/dock/protocoldock.h b/DSView/pv/dock/protocoldock.h index 248d1862..96d31124 100644 --- a/DSView/pv/dock/protocoldock.h +++ b/DSView/pv/dock/protocoldock.h @@ -34,15 +34,23 @@ #include #include #include +#include +#include #include #include +#include "../data/decodermodel.h" + namespace pv { class SigSession; +namespace data { +class DecoderModel; +} + namespace dock { class ProtocolDock : public QScrollArea @@ -58,19 +66,29 @@ public: void del_all_protocol(); signals: + void protocol_updated(); private slots: void add_protocol(); void rst_protocol(); void del_protocol(); + void set_model(); + void update_model(); + void export_table_view(); + void item_clicked(const QModelIndex &index); + void column_resize(int index, int old_size, int new_size); private: static int decoder_name_cmp(const void *a, const void *b); + void resize_table_view(data::DecoderModel *decoder_model); private: SigSession &_session; - QWidget *_widget; + QSplitter *_split_widget; + QWidget *_up_widget; + QWidget *_dn_widget; + QTableView *_table_view; QPushButton *_add_button; QPushButton *_del_all_button; @@ -80,7 +98,10 @@ private: QVector _protocol_label_list; QVector _protocol_index_list; QVector _hori_layout_list; - QVBoxLayout *_layout; + QVBoxLayout *_up_layout; + + QPushButton *_dn_set_button; + QPushButton *_dn_save_button; }; } // namespace dock diff --git a/DSView/pv/mainwindow.cpp b/DSView/pv/mainwindow.cpp index 2b708075..90417108 100644 --- a/DSView/pv/mainwindow.cpp +++ b/DSView/pv/mainwindow.cpp @@ -197,7 +197,9 @@ void MainWindow::setup_ui() SLOT(device_change())); connect(_dso_trigger_widget, SIGNAL(set_trig_pos(quint64)), _view, SLOT(set_trig_pos(quint64))); + connect(_protocol_widget, SIGNAL(protocol_updated()), _view, SLOT(signals_changed())); + setIconSize(QSize(40,40)); addToolBar(_sampling_bar); addToolBar(_trig_bar); addToolBar(_file_bar); diff --git a/DSView/pv/sigsession.cpp b/DSView/pv/sigsession.cpp index 19bf99f5..8b0802cf 100644 --- a/DSView/pv/sigsession.cpp +++ b/DSView/pv/sigsession.cpp @@ -41,6 +41,7 @@ #include "data/groupsnapshot.h" #include "data/decoderstack.h" #include "data/decode/decoder.h" +#include "data/decodermodel.h" #include "view/analogsignal.h" #include "view/dsosignal.h" @@ -99,6 +100,7 @@ SigSession::SigSession(DeviceManager &device_manager) : _refresh_timer.stop(); _refresh_timer.setSingleShot(true); _data_lock = false; + _decoder_model = new pv::data::DecoderModel(this); connect(this, SIGNAL(start_timer(int)), &_view_timer, SLOT(start(int))); //connect(&_view_timer, SIGNAL(timeout()), this, SLOT(refresh())); connect(&_refresh_timer, SIGNAL(timeout()), this, SLOT(data_unlock())); @@ -455,7 +457,6 @@ void SigSession::start_capture(bool instant, _view_timer.blockSignals(false); // Begin the session - _sampling_thread.reset(new boost::thread( &SigSession::sample_thread_proc, this, _dev_inst, error_handler)); @@ -1104,9 +1105,10 @@ void SigSession::data_feed_in(const struct sr_dev_inst *sdi, _cur_logic_snapshot.reset(); _cur_dso_snapshot.reset(); _cur_analog_snapshot.reset(); - +#ifdef ENABLE_DECODE BOOST_FOREACH(const boost::shared_ptr d, _decode_traces) d->frame_ended(); +#endif } frame_ended(); @@ -1394,6 +1396,12 @@ void SigSession::rst_decoder(view::DecodeTrace *signal) return; } } + +pv::data::DecoderModel* SigSession::get_decoder_model() const +{ + return _decoder_model; +} + #endif } // namespace pv diff --git a/DSView/pv/sigsession.h b/DSView/pv/sigsession.h index 23ad6a90..de159f17 100644 --- a/DSView/pv/sigsession.h +++ b/DSView/pv/sigsession.h @@ -67,6 +67,7 @@ class Logic; class LogicSnapshot; class Group; class GroupSnapshot; +class DecoderModel; } namespace device { @@ -154,6 +155,8 @@ public: void rst_decoder(view::DecodeTrace *signal); + pv::data::DecoderModel* get_decoder_model() const; + #endif void init_signals(); @@ -234,6 +237,7 @@ private: std::vector< boost::shared_ptr > _group_traces; #ifdef ENABLE_DECODE std::vector< boost::shared_ptr > _decode_traces; + pv::data::DecoderModel *_decoder_model; #endif mutable boost::mutex _data_mutex; @@ -291,6 +295,10 @@ signals: void zero_adj(); void progressSaveFileValueChanged(int percent); + void decode_done(); + + void show_region(uint64_t start, uint64_t end); + public slots: void reload(); void refresh(int holdtime); diff --git a/DSView/pv/toolbars/samplingbar.cpp b/DSView/pv/toolbars/samplingbar.cpp index 85b45603..ed758736 100644 --- a/DSView/pv/toolbars/samplingbar.cpp +++ b/DSView/pv/toolbars/samplingbar.cpp @@ -454,7 +454,8 @@ void SamplingBar::update_sample_rate_selector_value() break; } } - update_scale(); + if (samplerate != _sample_rate.itemData(_sample_rate.currentIndex()).value()) + update_scale(); _updating_sample_rate = false; } @@ -511,7 +512,7 @@ void SamplingBar::on_samplecount_sel(int index) g_variant_new_uint64(sample_count)); sample_count_changed(); - update_scale(); + //update_scale(); } } @@ -535,7 +536,7 @@ void SamplingBar::on_samplerate_sel(int index) SR_CONF_SAMPLERATE, g_variant_new_uint64(sample_rate)); - update_scale(); + //update_scale(); } } @@ -625,9 +626,11 @@ void SamplingBar::update_sample_count_selector_value() i).value()) _sample_count.setCurrentIndex(i); + if (samplecount != _sample_count.itemData(_sample_count.currentIndex()).value()) { + sample_count_changed(); + update_scale(); + } _updating_sample_count = false; - sample_count_changed(); - update_scale(); } void SamplingBar::commit_sample_count() diff --git a/DSView/pv/view/decodetrace.cpp b/DSView/pv/view/decodetrace.cpp index a8c84244..bd076af5 100644 --- a/DSView/pv/view/decodetrace.cpp +++ b/DSView/pv/view/decodetrace.cpp @@ -125,7 +125,6 @@ DecodeTrace::DecodeTrace(pv::SigSession &session, _end_index(0), _start_count(0), _end_count(0), - _show_hide_mapper(this), _popup_form(NULL), _popup() { @@ -137,8 +136,6 @@ DecodeTrace::DecodeTrace(pv::SigSession &session, this, SLOT(on_new_decode_data())); connect(_decoder_stack.get(), SIGNAL(decode_done()), this, SLOT(on_decode_done())); - connect(&_show_hide_mapper, SIGNAL(mapped(int)), - this, SLOT(on_show_hide_decoder(int))); } DecodeTrace::~DecodeTrace() @@ -203,7 +200,7 @@ void DecodeTrace::paint_back(QPainter &p, int left, int right) void DecodeTrace::paint_mid(QPainter &p, int left, int right) { - using namespace pv::data::decode; + using namespace pv::data::decode; const double scale = _view->scale(); assert(scale > 0); @@ -249,47 +246,57 @@ void DecodeTrace::paint_mid(QPainter &p, int left, int right) const double decode_startX = _decode_start/samples_per_pixel - (_view->offset() / _view->scale()); const double decode_endX = _decode_end/samples_per_pixel - (_view->offset() / _view->scale()); - const std::vector< std::pair > rows(_decoder_stack->get_visible_rows()); - for (size_t i = 0; i < rows.size(); i++) - { - const Row &row = rows[i].first; - const bool shown = rows[i].second; - if (!shown && _decoder_stack->has_annotations(row)) { - draw_unshown_row(p, y, annotation_height, decode_startX, decode_endX); - y += annotation_height; - _cur_row_headings.push_back(row.title()); - continue; - } + BOOST_FOREACH(boost::shared_ptr dec, + _decoder_stack->stack()) { + if (dec->shown()) { + const std::map& rows(_decoder_stack->get_rows_gshow()); + for (std::map::const_iterator i = rows.begin(); + i != rows.end(); i++) { + if ((*i).first.decoder() == dec->decoder() && + _decoder_stack->has_annotations((*i).first)) { + if ((*i).second) { + const Row &row = (*i).first; + size_t base_colour = 0x13579BDF; + boost::hash_combine(base_colour, this); + boost::hash_combine(base_colour, row.decoder()); + boost::hash_combine(base_colour, row.row()); + base_colour >>= 16; - size_t base_colour = 0x13579BDF; - boost::hash_combine(base_colour, this); - boost::hash_combine(base_colour, row.decoder()); - boost::hash_combine(base_colour, row.row()); - base_colour >>= 16; + const uint64_t min_annotation = + _decoder_stack->get_min_annotation(row); + const double min_annWidth = min_annotation / samples_per_pixel; - const uint64_t max_annotation = - _decoder_stack->get_max_annotation(row); - const double max_annWidth = max_annotation / samples_per_pixel; - if (max_annWidth > 5) { - vector annotations; - _decoder_stack->get_annotation_subset(annotations, row, - start_sample, end_sample); - if (!annotations.empty()) { - BOOST_FOREACH(const Annotation &a, annotations) - draw_annotation(a, p, get_text_colour(), - annotation_height, left, right, - samples_per_pixel, pixels_offset, y, - base_colour); + const uint64_t max_annotation = + _decoder_stack->get_max_annotation(row); + const double max_annWidth = max_annotation / samples_per_pixel; + if (max_annWidth > 5) { + vector annotations; + _decoder_stack->get_annotation_subset(annotations, row, + start_sample, end_sample); + if (!annotations.empty()) { + BOOST_FOREACH(const Annotation &a, annotations) + draw_annotation(a, p, get_text_colour(), + annotation_height, left, right, + samples_per_pixel, pixels_offset, y, + base_colour, min_annWidth); + } + } else if (max_annWidth != 0){ + draw_nodetail(p, annotation_height, left, right, y, base_colour); + } + if (max_annWidth != 0) { + y += annotation_height; + _cur_row_headings.push_back(row.title()); + } + } + } } - } else if (max_annWidth != 0){ - draw_nodetail(p, annotation_height, decode_startX, decode_endX, y, base_colour); - } - if (max_annWidth != 0) { + } else { + draw_unshown_row(p, y, annotation_height, left, right, tr("Unshown")); y += annotation_height; - _cur_row_headings.push_back(row.title()); + _cur_row_headings.push_back(dec->decoder()->name); } - } + } } void DecodeTrace::paint_fore(QPainter &p, int left, int right) @@ -350,8 +357,8 @@ bool DecodeTrace::create_popup() BOOST_FOREACH(boost::shared_ptr dec, _decoder_stack->stack()) { - if (dec->commit()) { - _decoder_stack->options_changed(true); + if (dec->commit() || _decoder_stack->options_changed()) { + _decoder_stack->set_options_changed(true); _decode_start = dec->decode_start(); _decode_end = dec->decode_end(); ret = true; @@ -378,6 +385,9 @@ void DecodeTrace::create_popup_form() _popup_form = new QFormLayout(_popup); _popup->setLayout(_popup_form); populate_popup_form(_popup, _popup_form); + const int width = _popup_form->sizeHint().width(); + const int height = _popup_form->sizeHint().height(); + _popup->resize(width, height); } void DecodeTrace::populate_popup_form(QWidget *parent, QFormLayout *form) @@ -395,20 +405,15 @@ void DecodeTrace::populate_popup_form(QWidget *parent, QFormLayout *form) const list< boost::shared_ptr >& stack = _decoder_stack->stack(); - if (stack.empty()) - { + if (stack.empty()) { QLabel *const l = new QLabel( tr("

No decoders in the stack

")); l->setAlignment(Qt::AlignCenter); form->addRow(l); - } - else - { - list< boost::shared_ptr >::const_iterator iter = - stack.begin(); - for (int i = 0; i < (int)stack.size(); i++, iter++) { - boost::shared_ptr dec(*iter); - create_decoder_form(i, dec, parent, form); + } else { + BOOST_FOREACH(boost::shared_ptr dec,stack) { + //boost::shared_ptr dec(*iter); + create_decoder_form(_decoder_stack, dec, parent, form); } form->addRow(new QLabel( @@ -477,9 +482,9 @@ void DecodeTrace::populate_popup_form(QWidget *parent, QFormLayout *form) } void DecodeTrace::draw_annotation(const pv::data::decode::Annotation &a, - QPainter &p, QColor text_color, int h, int left, int right, - double samples_per_pixel, double pixels_offset, int y, - size_t base_colour) const + QPainter &p, QColor text_color, int h, int left, int right, + double samples_per_pixel, double pixels_offset, int y, + size_t base_colour, double min_annWidth) const { const double start = max(a.start_sample() / samples_per_pixel - pixels_offset, (double)left); @@ -495,7 +500,7 @@ void DecodeTrace::draw_annotation(const pv::data::decode::Annotation &a, if (a.start_sample() == a.end_sample()) draw_instant(a, p, fill, outline, text_color, h, - start, y); + start, y, min_annWidth); else draw_range(a, p, fill, outline, text_color, h, start, end, y); @@ -506,22 +511,31 @@ void DecodeTrace::draw_nodetail(QPainter &p, size_t base_colour) const { const QRectF nodetail_rect(left, y - h/2 + 0.5, right - left, h); - const size_t colour = base_colour % countof(Colours); - const QColor &fill = Colours[colour]; + QString info = tr("Zoom in For Detial"); + int info_left = nodetail_rect.center().x() - p.boundingRect(QRectF(), 0, info).width(); + int info_right = nodetail_rect.center().x() + p.boundingRect(QRectF(), 0, info).width(); + int height = p.boundingRect(QRectF(), 0, info).height(); - p.setPen(Qt::white); - p.setBrush(fill); - p.drawRect(nodetail_rect); - p.drawText(nodetail_rect, Qt::AlignCenter | Qt::AlignVCenter, "Zoom in for more detials"); + p.setPen(Trace::DARK_FORE); + p.drawLine(left, y, info_left, y); + p.drawLine(info_right, y, right, y); + p.drawLine(info_left, y, info_left+5, y - height/2 + 0.5); + p.drawLine(info_left, y, info_left+5, y + height/2 + 0.5); + p.drawLine(info_right, y, info_right-5, y - height/2 + 0.5); + p.drawLine(info_right, y, info_right-5, y + height/2 + 0.5); + + p.setPen(Trace::DARK_FORE); + p.drawText(nodetail_rect, Qt::AlignCenter | Qt::AlignVCenter, info); } void DecodeTrace::draw_instant(const pv::data::decode::Annotation &a, QPainter &p, - QColor fill, QColor outline, QColor text_color, int h, double x, int y) const + QColor fill, QColor outline, QColor text_color, int h, double x, int y, double min_annWidth) const { const QString text = a.annotations().empty() ? QString() : a.annotations().back(); - const double w = min((double)p.boundingRect(QRectF(), 0, text).width(), - 0.0) + h; +// const double w = min((double)p.boundingRect(QRectF(), 0, text).width(), +// 0.0) + h; + const double w = min(min_annWidth, (double)h); const QRectF rect(x - w / 2, y - h / 2, w, h); p.setPen(outline); @@ -564,7 +578,7 @@ void DecodeTrace::draw_range(const pv::data::decode::Annotation &a, QPainter &p, QPointF(start + cap_width, bottom) }; - p.setPen(Qt::white); + p.setPen(DARK_BACK); p.drawConvexPolygon(pts, countof(pts)); if (annotations.empty()) @@ -643,20 +657,8 @@ bool DecodeTrace::draw_unresolved_period(QPainter &p, int h, int left, return false; const int y = get_y(); -// const double start = max(samples_decoded / -// samples_per_pixel - pixels_offset, left - 1.0); -// const double end = min(sample_count / samples_per_pixel - -// pixels_offset, right + 1.0); const QRectF no_decode_rect(left, y - h/2 + 0.5, right - left, h); - p.setPen(QPen(Qt::NoPen)); - p.setBrush(Qt::white); - p.drawRect(no_decode_rect); - - p.setPen(NoDecodeColour); - p.setBrush(QBrush(NoDecodeColour, Qt::Dense7Pattern)); - p.drawRect(no_decode_rect); - const int progress100 = ceil(samples_decoded * 100.0 / need_sample_count); p.setPen(dsLightBlue); QFont font=p.font(); @@ -669,40 +671,40 @@ bool DecodeTrace::draw_unresolved_period(QPainter &p, int h, int left, } void DecodeTrace::draw_unshown_row(QPainter &p, int y, int h, int left, - int right) + int right, QString info) { const QRectF unshown_rect(left, y - h/2 + 0.5, right - left, h); + int info_left = unshown_rect.center().x() - p.boundingRect(QRectF(), 0, info).width(); + int info_right = unshown_rect.center().x() + p.boundingRect(QRectF(), 0, info).width(); + int height = p.boundingRect(QRectF(), 0, info).height(); - p.setPen(QPen(Qt::NoPen)); - p.setBrush(QBrush(NoDecodeColour, Qt::Dense7Pattern)); - p.drawRect(unshown_rect); + p.setPen(Trace::DARK_FORE); + p.drawLine(left, y, info_left, y); + p.drawLine(info_right, y, right, y); + p.drawLine(info_left, y, info_left+5, y - height/2 + 0.5); + p.drawLine(info_left, y, info_left+5, y + height/2 + 0.5); + p.drawLine(info_right, y, info_right-5, y - height/2 + 0.5); + p.drawLine(info_right, y, info_right-5, y + height/2 + 0.5); - p.setPen(dsLightBlue); - QFont font=p.font(); - font.setPointSize(_view->get_signalHeight()*2/3); - font.setBold(true); - p.setFont(font); - p.drawText(unshown_rect, Qt::AlignCenter | Qt::AlignVCenter, "Unshown"); + p.setPen(Trace::DARK_FORE); + p.drawText(unshown_rect, Qt::AlignCenter | Qt::AlignVCenter, info); } -void DecodeTrace::create_decoder_form(int index, +void DecodeTrace::create_decoder_form( + boost::shared_ptr &decoder_stack, boost::shared_ptr &dec, QWidget *parent, - QFormLayout *form) + QFormLayout *form) { const GSList *l; - assert(dec); + assert(dec); const srd_decoder *const decoder = dec->decoder(); assert(decoder); - pv::widgets::DecoderGroupBox *const group = - new pv::widgets::DecoderGroupBox( - QString::fromUtf8(decoder->name)); - group->set_decoder_visible(dec->shown()); - - _show_hide_mapper.setMapping(group, index); - connect(group, SIGNAL(show_hide_decoder()), - &_show_hide_mapper, SLOT(map())); + pv::widgets::DecoderGroupBox *const group = + new pv::widgets::DecoderGroupBox(decoder_stack, dec); + connect(group, SIGNAL(del_stack(boost::shared_ptr&)), + this, SLOT(on_del_stack(boost::shared_ptr&))); QFormLayout *const decoder_form = new QFormLayout; group->add_layout(decoder_form); @@ -739,7 +741,7 @@ void DecodeTrace::create_decoder_form(int index, // Add the options boost::shared_ptr binding( - new prop::binding::DecoderOptions(_decoder_stack, dec)); + new prop::binding::DecoderOptions(decoder_stack, dec)); binding->add_properties_to_form(decoder_form, true); _bindings.push_back(binding); @@ -837,6 +839,7 @@ void DecodeTrace::on_decode_done() _view->set_need_update(true); _view->signals_changed(); } + _session.decode_done(); } void DecodeTrace::on_delete() @@ -860,33 +863,36 @@ void DecodeTrace::on_stack_decoder(srd_decoder *decoder) create_popup_form(); } -void DecodeTrace::on_show_hide_decoder(int index) +void DecodeTrace::on_del_stack(boost::shared_ptr &dec) { - using pv::data::decode::Decoder; + assert(dec); + assert(_decoder_stack); + _decoder_stack->remove(dec); - const list< boost::shared_ptr > stack(_decoder_stack->stack()); - - // Find the decoder in the stack - list< boost::shared_ptr >::const_iterator iter = stack.begin(); - for(int i = 0; i < index; i++, iter++) - assert(iter != stack.end()); - - boost::shared_ptr dec = *iter; - assert(dec); - - const bool show = !dec->shown(); - dec->show(show); - - assert(index < (int)_decoder_forms.size()); - _decoder_forms[index]->set_decoder_visible(show); - - //_view->set_need_update(true); + create_popup_form(); } - int DecodeTrace::rows_size() { - return _decoder_stack->cur_rows_size(); + using pv::data::decode::Decoder; + + int size = 0; + BOOST_FOREACH(boost::shared_ptr dec, + _decoder_stack->stack()) { + if (dec->shown()) { + const std::map& rows(_decoder_stack->get_rows_gshow()); + for (std::map::const_iterator i = rows.begin(); + i != rows.end(); i++) { + if ((*i).first.decoder() == dec->decoder() && + _decoder_stack->has_annotations((*i).first) && + (*i).second) + size++; + } + } else { + size++; + } + } + return size == 0 ? 1 : size; } void DecodeTrace::paint_type_options(QPainter &p, int right, const QPoint pt) @@ -897,9 +903,8 @@ void DecodeTrace::paint_type_options(QPainter &p, int right, const QPoint pt) const QRectF group_index_rect = get_rect(CHNLREG, y, right); QString index_string; int last_index; - p.setPen(Qt::transparent); - p.setBrush(dsBlue); - p.drawRect(group_index_rect); + p.setPen(QPen(DARK_FORE, 1, Qt::DashLine)); + p.drawLine(group_index_rect.bottomLeft(), group_index_rect.bottomRight()); std::list::iterator i = _index_list.begin(); last_index = (*i); index_string = QString::number(last_index); @@ -912,7 +917,7 @@ void DecodeTrace::paint_type_options(QPainter &p, int right, const QPoint pt) index_string = QString::number((*i)) + "," + index_string; last_index = (*i); } - p.setPen(Qt::white); + p.setPen(DARK_FORE); p.drawText(group_index_rect, Qt::AlignRight | Qt::AlignVCenter, index_string); } @@ -986,5 +991,6 @@ void DecodeTrace::frame_ended() } } + } // namespace view } // namespace pv diff --git a/DSView/pv/view/decodetrace.h b/DSView/pv/view/decodetrace.h index 32f17bd9..553da4d9 100644 --- a/DSView/pv/view/decodetrace.h +++ b/DSView/pv/view/decodetrace.h @@ -152,14 +152,14 @@ private: void draw_annotation(const pv::data::decode::Annotation &a, QPainter &p, QColor text_colour, int text_height, int left, int right, double samples_per_pixel, double pixels_offset, int y, - size_t base_colour) const; + size_t base_colour, double min_annWidth) const; void draw_nodetail(QPainter &p, int text_height, int left, int right, int y, size_t base_colour) const; void draw_instant(const pv::data::decode::Annotation &a, QPainter &p, QColor fill, QColor outline, QColor text_color, int h, double x, - int y) const; + int y, double min_annWidth) const; void draw_range(const pv::data::decode::Annotation &a, QPainter &p, QColor fill, QColor outline, QColor text_color, int h, double start, @@ -172,11 +172,11 @@ private: int right); void draw_unshown_row(QPainter &p, int y, int h, int left, - int right); + int right, QString info); - void create_decoder_form(int index, - boost::shared_ptr &dec, - QWidget *parent, QFormLayout *form); + void create_decoder_form(boost::shared_ptr &decoder_stack, + boost::shared_ptr &dec, + QWidget *parent, QFormLayout *form); QComboBox* create_probe_selector(QWidget *parent, const boost::shared_ptr &dec, @@ -195,8 +195,7 @@ private slots: void on_probe_selected(int); void on_stack_decoder(srd_decoder *decoder); - - void on_show_hide_decoder(int index); + void on_del_stack(boost::shared_ptr &dec); void on_decode_done(); @@ -219,7 +218,6 @@ private: std::vector _cur_row_headings; - QSignalMapper _show_hide_mapper; QFormLayout *_popup_form; QDialog *_popup; }; diff --git a/DSView/pv/view/devmode.cpp b/DSView/pv/view/devmode.cpp index f48c453c..2fb0a2b7 100644 --- a/DSView/pv/view/devmode.cpp +++ b/DSView/pv/view/devmode.cpp @@ -44,45 +44,50 @@ using namespace std; namespace pv { namespace view { -DevMode::DevMode(View &parent) : - QWidget(&parent), - _view(parent), - layout(new QGridLayout(this)) +DevMode::DevMode(QWidget *parent, SigSession &session) : + QWidget(parent), + _session(session), + _layout(new QGridLayout(this)) { - setLayout(layout); + setLayout(_layout); } void DevMode::set_device() { int index = 0; - const boost::shared_ptr dev_inst = _view.session().get_device(); + const boost::shared_ptr dev_inst = _session.get_device(); assert(dev_inst); + for(std::map::const_iterator i = _mode_button_list.begin(); + i != _mode_button_list.end(); i++) { + (*i).first->setParent(NULL); + _layout->removeWidget((*i).first); + delete (*i).first; + } _mode_button_list.clear(); - delete layout; - layout = new QGridLayout(this); for (GSList *l = dev_inst->get_dev_mode_list(); l; l = l->next) { sr_dev_mode *mode = (sr_dev_mode *)l->data; - boost::shared_ptr mode_button = boost::shared_ptr(new QPushButton(NULL)); - mode_button->setFlat(true); + QPushButton *mode_button = new QPushButton(this); + //mode_button->setFlat(true); mode_button->setText(mode->name); + mode_button->setCheckable(true); _mode_button_list[mode_button] = mode; + if (dev_inst->dev_inst()->mode == _mode_button_list[mode_button]->mode) + mode_button->setChecked(true); - connect(mode_button.get(), SIGNAL(clicked()), this, SLOT(on_mode_change())); + connect(mode_button, SIGNAL(clicked()), this, SLOT(on_mode_change())); - layout->addWidget(mode_button.get(), index / GRID_COLS, index % GRID_COLS); - layout->addWidget(new QWidget(), index / GRID_COLS, GRID_COLS); - layout->setColumnStretch(GRID_COLS, 1); + _layout->addWidget(mode_button, index / GRID_COLS, index % GRID_COLS); + //layout->addWidget(new QWidget(), index / GRID_COLS, GRID_COLS); + _layout->setColumnStretch(GRID_COLS, 1); index++; - } - - setLayout(layout); + } update(); } @@ -94,40 +99,27 @@ void DevMode::paintEvent(QPaintEvent*) o.initFrom(this); QPainter painter(this); style()->drawPrimitive(QStyle::PE_Widget, &o, &painter, this); - - painter.setRenderHint(QPainter::Antialiasing); - painter.setPen(Qt::NoPen); - for(std::map, sr_dev_mode *>::const_iterator i = _mode_button_list.begin(); - i != _mode_button_list.end(); i++) { - const boost::shared_ptr dev_inst = _view.session().get_device(); - assert(dev_inst); - if (dev_inst->dev_inst()->mode == (*i).second->mode) - painter.setBrush(Trace::dsBlue); - else - painter.setBrush(Trace::dsGray); - - painter.drawRoundedRect((*i).first->geometry(), 4, 4); - } - - painter.end(); } void DevMode::on_mode_change() { - const boost::shared_ptr dev_inst = _view.session().get_device(); + const boost::shared_ptr dev_inst = _session.get_device(); assert(dev_inst); QPushButton *button = qobject_cast(sender()); - for(std::map, sr_dev_mode *>::const_iterator i = _mode_button_list.begin(); + for(std::map::const_iterator i = _mode_button_list.begin(); i != _mode_button_list.end(); i++) { - if ((*i).first.get() == button) { + if ((*i).first == button) { if (dev_inst->dev_inst()->mode != (*i).second->mode) { - _view.session().stop_capture(); + _session.stop_capture(); dev_inst->set_config(NULL, NULL, SR_CONF_DEVICE_MODE, g_variant_new_int16((*i).second->mode)); + button->setChecked(true); mode_changed(); } + } else { + (*i).first->setChecked(false); } } } diff --git a/DSView/pv/view/devmode.h b/DSView/pv/view/devmode.h index 27488805..3e114fbb 100644 --- a/DSView/pv/view/devmode.h +++ b/DSView/pv/view/devmode.h @@ -44,9 +44,9 @@ namespace device{ class DevInst; } -namespace view { +class SigSession; -class View; +namespace view { class DevMode : public QWidget { @@ -56,7 +56,7 @@ private: static const int GRID_COLS = 3; public: - DevMode(View &parent); + DevMode(QWidget *parent, SigSession &session); private: void paintEvent(QPaintEvent *event); @@ -77,10 +77,10 @@ signals: void mode_changed(); private: - View &_view; + SigSession &_session; - QGridLayout * layout; - std::map , sr_dev_mode *> _mode_button_list; + QGridLayout * _layout; + std::map _mode_button_list; QPoint _mouse_point; }; diff --git a/DSView/pv/view/groupsignal.cpp b/DSView/pv/view/groupsignal.cpp index 0c674dfd..aec24c5b 100644 --- a/DSView/pv/view/groupsignal.cpp +++ b/DSView/pv/view/groupsignal.cpp @@ -204,9 +204,8 @@ void GroupSignal::paint_type_options(QPainter &p, int right, const QPoint pt) const QRectF group_index_rect = get_rect(CHNLREG, y, right); QString index_string; int last_index; - p.setPen(Qt::transparent); - p.setBrush(dsBlue); - p.drawRect(group_index_rect); + p.setPen(QPen(DARK_FORE, 1, Qt::DashLine)); + p.drawLine(group_index_rect.bottomLeft(), group_index_rect.bottomRight()); std::list::iterator i = _index_list.begin(); last_index = (*i); index_string = QString::number(last_index); @@ -219,7 +218,7 @@ void GroupSignal::paint_type_options(QPainter &p, int right, const QPoint pt) index_string = QString::number((*i)) + "," + index_string; last_index = (*i); } - p.setPen(Qt::white); + p.setPen(DARK_FORE); p.drawText(group_index_rect, Qt::AlignRight | Qt::AlignVCenter, index_string); } diff --git a/DSView/pv/view/header.cpp b/DSView/pv/view/header.cpp index e6d20c37..ca38299f 100644 --- a/DSView/pv/view/header.cpp +++ b/DSView/pv/view/header.cpp @@ -115,15 +115,13 @@ void Header::paintEvent(QPaintEvent*) QStyleOption o; o.initFrom(this); QPainter painter(this); + //painter.setRenderHint(QPainter::Antialiasing); style()->drawPrimitive(QStyle::PE_Widget, &o, &painter, this); const int w = width(); const vector< boost::shared_ptr > traces( _view.get_traces()); - //QPainter painter(this); - //painter.setRenderHint(QPainter::Antialiasing); - const bool dragging = !_drag_traces.empty(); BOOST_FOREACH(const boost::shared_ptr t, traces) { diff --git a/DSView/pv/view/logicsignal.cpp b/DSView/pv/view/logicsignal.cpp index 4bbe9ad0..00df932b 100644 --- a/DSView/pv/view/logicsignal.cpp +++ b/DSView/pv/view/logicsignal.cpp @@ -44,24 +44,7 @@ const QColor LogicSignal::EdgeColour(0x80, 0x80, 0x80); const QColor LogicSignal::HighColour(0x00, 0xC0, 0x00); const QColor LogicSignal::LowColour(0xC0, 0x00, 0x00); -const QColor LogicSignal::SignalColours[8] = { - QColor(0x16, 0x19, 0x1A), // Black - QColor(0x8F, 0x52, 0x02), // Brown - QColor(0xCC, 0x00, 0x00), // Red - QColor(0xF5, 0x79, 0x00), // Orange - QColor(0xED, 0xD4, 0x00), // Yellow - QColor(0x73, 0xD2, 0x16), // Green - QColor(0x34, 0x65, 0xA4), // Blue - QColor(0x75, 0x50, 0x7B), // Violet -// QColor(17, 133, 209), -// QColor(17, 133, 209), -// QColor(17, 133, 209), -// QColor(17, 133, 209), -// QColor(17, 133, 209), -// QColor(17, 133, 209), -// QColor(17, 133, 209), -// QColor(17, 133, 209), -}; +const QColor LogicSignal::DEFAULT_COLOR = QColor(150, 150, 150, 255); const int LogicSignal::StateHeight = 12; const int LogicSignal::StateRound = 5; @@ -73,7 +56,8 @@ LogicSignal::LogicSignal(boost::shared_ptr dev_inst, _data(data), _trig(NONTRIG) { - _colour = SignalColours[probe->index % countof(SignalColours)]; + //_colour = PROBE_COLORS[probe->index % countof(PROBE_COLORS)]; + _colour = DEFAULT_COLOR; } LogicSignal::LogicSignal(boost::shared_ptr s, @@ -249,35 +233,37 @@ void LogicSignal::paint_type_options(QPainter &p, int right, const QPoint pt) const QRectF lowTrig_rect = get_rect(LOWTRIG, y, right); const QRectF edgeTrig_rect = get_rect(EDGTRIG, y, right); - p.setPen(Qt::transparent); - p.setBrush(posTrig_rect.contains(pt) ? dsYellow.darker() : - (_trig == POSTRIG) ? dsYellow : dsBlue); + p.setPen(Qt::NoPen); + p.setBrush(posTrig_rect.contains(pt) ? dsBlue.lighter() : + (_trig == POSTRIG) ? dsBlue : DARK_BACK); p.drawRect(posTrig_rect); - p.setBrush(higTrig_rect.contains(pt) ? dsYellow.darker() : - (_trig == HIGTRIG) ? dsYellow : dsBlue); + p.setBrush(higTrig_rect.contains(pt) ? dsBlue.lighter() : + (_trig == HIGTRIG) ? dsBlue : DARK_BACK); p.drawRect(higTrig_rect); - p.setBrush(negTrig_rect.contains(pt) ? dsYellow.darker() : - (_trig == NEGTRIG) ? dsYellow : dsBlue); + p.setBrush(negTrig_rect.contains(pt) ? dsBlue.lighter() : + (_trig == NEGTRIG) ? dsBlue : DARK_BACK); p.drawRect(negTrig_rect); - p.setBrush(lowTrig_rect.contains(pt) ? dsYellow.darker() : - (_trig == LOWTRIG) ? dsYellow : dsBlue); + p.setBrush(lowTrig_rect.contains(pt) ? dsBlue.lighter() : + (_trig == LOWTRIG) ? dsBlue : DARK_BACK); p.drawRect(lowTrig_rect); - p.setBrush(edgeTrig_rect.contains(pt) ? dsYellow.darker() : - (_trig == EDGTRIG) ? dsYellow : dsBlue); + p.setBrush(edgeTrig_rect.contains(pt) ? dsBlue.lighter() : + (_trig == EDGTRIG) ? dsBlue : DARK_BACK); p.drawRect(edgeTrig_rect); - p.setPen(QPen(Qt::blue, 1, Qt::DotLine)); + p.setPen(QPen(DARK_FORE, 1, Qt::DashLine)); p.setBrush(Qt::transparent); - p.drawLine(posTrig_rect.right(), posTrig_rect.top() + 3, - posTrig_rect.right(), posTrig_rect.bottom() - 3); - p.drawLine(higTrig_rect.right(), higTrig_rect.top() + 3, - higTrig_rect.right(), higTrig_rect.bottom() - 3); - p.drawLine(negTrig_rect.right(), negTrig_rect.top() + 3, - negTrig_rect.right(), negTrig_rect.bottom() - 3); - p.drawLine(lowTrig_rect.right(), lowTrig_rect.top() + 3, - lowTrig_rect.right(), lowTrig_rect.bottom() - 3); +// p.drawLine(posTrig_rect.right(), posTrig_rect.top(), +// posTrig_rect.right(), posTrig_rect.bottom()); +// p.drawLine(higTrig_rect.right(), higTrig_rect.top(), +// higTrig_rect.right(), higTrig_rect.bottom()); +// p.drawLine(negTrig_rect.right(), negTrig_rect.top(), +// negTrig_rect.right(), negTrig_rect.bottom()); +// p.drawLine(lowTrig_rect.right(), lowTrig_rect.top(), +// lowTrig_rect.right(), lowTrig_rect.bottom()); + p.drawLine(posTrig_rect.left(), posTrig_rect.bottom(), + edgeTrig_rect.right(), edgeTrig_rect.bottom()); - p.setPen(QPen(Qt::white, 2, Qt::SolidLine)); + p.setPen(QPen(DARK_FORE, 2, Qt::SolidLine)); p.setBrush(Qt::transparent); p.drawLine(posTrig_rect.left() + 5, posTrig_rect.bottom() - 5, posTrig_rect.center().x(), posTrig_rect.bottom() - 5); diff --git a/DSView/pv/view/logicsignal.h b/DSView/pv/view/logicsignal.h index 5f4ba7d1..3daa567b 100644 --- a/DSView/pv/view/logicsignal.h +++ b/DSView/pv/view/logicsignal.h @@ -48,7 +48,7 @@ private: static const QColor HighColour; static const QColor LowColour; - static const QColor SignalColours[8]; + static const QColor DEFAULT_COLOR; static const int StateHeight; static const int StateRound; diff --git a/DSView/pv/view/ruler.cpp b/DSView/pv/view/ruler.cpp index 0a3b8015..3fdab4b9 100644 --- a/DSView/pv/view/ruler.cpp +++ b/DSView/pv/view/ruler.cpp @@ -77,6 +77,7 @@ const QColor Ruler::dsBlue = QColor(17, 133, 209, 255); const QColor Ruler::dsYellow = QColor(238, 178, 17, 255); const QColor Ruler::dsRed = QColor(213, 15, 37, 255); const QColor Ruler::dsGreen = QColor(0, 153, 37, 255); +const QColor Ruler::RULER_COLOR = QColor(255, 255, 255, 255); const QColor Ruler::HitColor = dsYellow; const QColor Ruler::WarnColor = dsRed; @@ -458,7 +459,7 @@ void Ruler::draw_logic_tick_mark(QPainter &p) AlignLeft | AlignTop, "8").height(); // Draw the tick marks - p.setPen(dsBlue); + p.setPen(Trace::DARK_FORE); const double minor_tick_period = tick_period / MinPeriodScale; const int minor_order = (int)floorf(log10f(minor_tick_period)); @@ -548,7 +549,7 @@ void Ruler::draw_hover_mark(QPainter &p) return; p.setPen(QPen(Qt::NoPen)); - p.setBrush(dsBlue); + p.setBrush(RULER_COLOR); const int b = height() - 1; const QPointF points[] = { diff --git a/DSView/pv/view/ruler.h b/DSView/pv/view/ruler.h index c22d8cce..d3389bdc 100644 --- a/DSView/pv/view/ruler.h +++ b/DSView/pv/view/ruler.h @@ -54,6 +54,7 @@ private: static const QColor dsYellow; static const QColor dsRed; static const QColor dsGreen; + static const QColor RULER_COLOR; public: static const QColor CursorColor[8]; diff --git a/DSView/pv/view/trace.cpp b/DSView/pv/view/trace.cpp index 52d522d5..3c79bc1a 100644 --- a/DSView/pv/view/trace.cpp +++ b/DSView/pv/view/trace.cpp @@ -50,6 +50,29 @@ const QColor Trace::dsLightBlue = QColor(17, 133, 209, 150); const QColor Trace::dsLightRed = QColor(213, 15, 37, 150); const QPen Trace::SignalAxisPen = QColor(128, 128, 128, 64); +const QColor Trace::DARK_BACK = QColor(48, 47, 47, 255); +const QColor Trace::DARK_FORE = QColor(150, 150, 150, 255); +const QColor Trace::DARK_HIGHLIGHT = QColor(32, 32, 32, 255); + +const QColor Trace::PROBE_COLORS[8] = { + QColor(0x50, 0x50, 0x50), // Black + QColor(0x8F, 0x52, 0x02), // Brown + QColor(0xCC, 0x00, 0x00), // Red + QColor(0xF5, 0x79, 0x00), // Orange + QColor(0xED, 0xD4, 0x00), // Yellow + QColor(0x73, 0xD2, 0x16), // Green + QColor(0x34, 0x65, 0xA4), // Blue + QColor(0x75, 0x50, 0x7B), // Violet +// QColor(17, 133, 209), +// QColor(17, 133, 209), +// QColor(17, 133, 209), +// QColor(17, 133, 209), +// QColor(17, 133, 209), +// QColor(17, 133, 209), +// QColor(17, 133, 209), +// QColor(17, 133, 209), +}; + const QPen Trace::AxisPen(QColor(128, 128, 128, 64)); const int Trace::LabelHitPadding = 2; @@ -220,14 +243,14 @@ void Trace::paint_label(QPainter &p, int right, const QPoint pt) const QRectF name_rect = get_rect("name", y, right); const QRectF label_rect = get_rect("label", get_zeroPos(), right); - p.setRenderHint(QPainter::Antialiasing); + //p.setRenderHint(QPainter::Antialiasing); // Paint the ColorButton p.setPen(Qt::transparent); p.setBrush(enabled() ? _colour : dsDisable); p.drawRect(color_rect); // Paint the signal name - p.setPen(enabled() ? Qt::black : dsDisable); + p.setPen(enabled() ? DARK_FORE: dsDisable); p.drawText(name_rect, Qt::AlignLeft | Qt::AlignVCenter, _name); // Paint the trigButton @@ -244,16 +267,14 @@ void Trace::paint_label(QPainter &p, int right, const QPoint pt) }; p.setPen(Qt::transparent); - if (_type == SR_CHANNEL_DSO) + if (_type == SR_CHANNEL_DSO) { p.setBrush((label_rect.contains(pt) || selected()) ? _colour.darker() : _colour); - else - p.setBrush((label_rect.contains(pt) || selected()) ? dsYellow : dsBlue); - p.drawPolygon(points, countof(points)); - - p.setPen(QPen(Qt::blue, 1, Qt::DotLine)); - p.setBrush(Qt::transparent); - p.drawLine(label_rect.right(), label_rect.top() + 3, - label_rect.right(), label_rect.bottom() - 3); + p.drawPolygon(points, countof(points)); + } else { + QColor color = PROBE_COLORS[*_index_list.begin() % countof(PROBE_COLORS)]; + p.setBrush((label_rect.contains(pt) || selected()) ? color.lighter() : color); + p.drawPolygon(points, countof(points)); + } // Paint the text p.setPen(Qt::white); @@ -326,30 +347,6 @@ void Trace::compute_text_size(QPainter &p) p.boundingRect(QRectF(), 0, "99").height()); } -QRectF Trace::get_rect(const char *s, int y, int right) -{ - const QSizeF color_size(SquareWidth, SquareWidth); - const QSizeF name_size(right - get_leftWidth() - get_rightWidth(), SquareWidth); - //const QSizeF label_size(_text_size.width() + Margin, SquareWidth); - const QSizeF label_size(SquareWidth, SquareWidth); - - if (!strcmp(s, "name")) - return QRectF( - get_leftWidth(), - y - name_size.height() / 2, - name_size.width(), name_size.height()); - else if (!strcmp(s, "label")) - return QRectF( - right - 1.5f * label_size.width(), - y - SquareWidth / 2, - label_size.width(), label_size.height()); - else - return QRectF( - 2, - y - SquareWidth / 2, - SquareWidth, SquareWidth); -} - QRectF Trace::get_view_rect() const { assert(_view); @@ -385,7 +382,7 @@ int Trace::rows_size() int Trace::get_leftWidth() const { - return SquareWidth + Margin; + return SquareWidth/2 + Margin; } int Trace::get_rightWidth() const @@ -398,5 +395,34 @@ int Trace::get_headerHeight() const return SquareWidth; } +QRectF Trace::get_rect(const char *s, int y, int right) const +{ + const QSizeF color_size(get_leftWidth() - Margin, SquareWidth); + const QSizeF name_size(right - get_leftWidth() - get_rightWidth(), SquareWidth); + //const QSizeF label_size(_text_size.width() + Margin, SquareWidth); + const QSizeF label_size(SquareWidth, SquareWidth); + + if (!strcmp(s, "name")) + return QRectF( + get_leftWidth(), + y - name_size.height() / 2, + name_size.width(), name_size.height()); + else if (!strcmp(s, "label")) + return QRectF( + right - 1.5f * label_size.width(), + y - label_size.height() / 2, + label_size.width(), label_size.height()); + else if (!strcmp(s, "color")) + return QRectF( + 2, + y - color_size.height() / 2, + color_size.width(), color_size.height()); + else + return QRectF( + 2, + y - SquareWidth / 2, + SquareWidth, SquareWidth); +} + } // namespace view } // namespace pv diff --git a/DSView/pv/view/trace.h b/DSView/pv/view/trace.h index 329400bd..26d9b504 100644 --- a/DSView/pv/view/trace.h +++ b/DSView/pv/view/trace.h @@ -70,6 +70,12 @@ public: static const QColor dsLightRed; static const QPen SignalAxisPen; + static const QColor DARK_BACK; + static const QColor DARK_FORE; + static const QColor DARK_HIGHLIGHT; + + static const QColor PROBE_COLORS[8]; + protected: Trace(QString name, uint16_t index, int type); Trace(QString name, std::list index_list, int type, int sec_index); @@ -225,7 +231,7 @@ public: * area. * @return Returns the rectangle of the signal label. */ - QRectF get_rect(const char *s, int y, int right); + QRectF get_rect(const char *s, int y, int right) const; virtual int rows_size(); diff --git a/DSView/pv/view/view.cpp b/DSView/pv/view/view.cpp index e421a8ce..df0e2e87 100644 --- a/DSView/pv/view/view.cpp +++ b/DSView/pv/view/view.cpp @@ -74,7 +74,7 @@ View::View(SigSession &session, pv::toolbars::SamplingBar *sampling_bar, QWidget _viewport(new Viewport(*this)), _ruler(new Ruler(*this)), _header(new Header(*this)), - _devmode(new DevMode(*this)), + _devmode(new DevMode(this, session)), _scale(1e-8), _preScale(1e-6), _maxscale(1e9), @@ -98,15 +98,17 @@ View::View(SigSession &session, pv::toolbars::SamplingBar *sampling_bar, QWidget setViewportMargins(headerWidth(), RulerHeight, 0, 0); setViewport(_viewport); + connect(&_session, SIGNAL(device_setted()), + _devmode, SLOT(set_device())); connect(&_session, SIGNAL(signals_changed()), this, SLOT(signals_changed())); connect(&_session, SIGNAL(data_updated()), this, SLOT(data_updated())); connect(&_session, SIGNAL(receive_trigger(quint64)), this, SLOT(set_trig_pos(quint64))); + connect(&_session, SIGNAL(show_region(uint64_t,uint64_t)), + this, SLOT(show_region(uint64_t, uint64_t))); - connect(&_session, SIGNAL(device_setted()), - _devmode, SLOT(set_device())); connect(_devmode, SIGNAL(mode_changed()), this, SIGNAL(mode_changed())); @@ -464,9 +466,9 @@ void View::update_scale() } _minscale = (1.0 / sample_rate) / MaxPixelsPerSample; - _offset = 0; + //_offset = 0; _preScale = _scale; - _preOffset = _offset; + //_preOffset = _offset; _trig_cursor->set_index(_trig_pos); @@ -514,6 +516,7 @@ void View::signals_changed() _viewport->clear_measure(); header_updated(); normalize_layout(); + data_updated(); } bool View::eventFilter(QObject *object, QEvent *event) @@ -834,5 +837,14 @@ QString View::trigger_time() return _trigger_time.toString("yyyy-MM-dd hh:mm:ss ddd"); } +void View::show_region(uint64_t start, uint64_t end) +{ + assert(start <= end); + const double ideal_scale = (end-start) * 2.0 / _session.get_device()->get_sample_rate() / get_view_width(); + const double new_scale = max(min(ideal_scale, _maxscale), _minscale); + const double new_off = (start + end) * 0.5 / _session.get_device()->get_sample_rate() - new_scale * get_view_width() / 2; + set_scale_offset(new_scale, new_off); +} + } // namespace view } // namespace pv diff --git a/DSView/pv/view/view.h b/DSView/pv/view/view.h index 27fabea0..1f903737 100644 --- a/DSView/pv/view/view.h +++ b/DSView/pv/view/view.h @@ -221,6 +221,7 @@ public slots: void signals_changed(); void data_updated(); void update_scale(); + void show_region(uint64_t start, uint64_t end); private slots: diff --git a/DSView/pv/view/viewport.cpp b/DSView/pv/view/viewport.cpp index c53e5d51..4f5b6f67 100644 --- a/DSView/pv/view/viewport.cpp +++ b/DSView/pv/view/viewport.cpp @@ -445,11 +445,11 @@ void Viewport::mouseMoveEvent(QMouseEvent *event) if (_action_type == DSO_YM) _dso_ym_end = event->pos().y(); - - measure(); } _mouse_point = event->pos(); + + measure(); update(); } @@ -719,7 +719,6 @@ void Viewport::leaveEvent(QEvent *) _mm_period = "#####"; _mm_freq = "#####"; _mm_duty = "#####"; - measure_updated(); } else if (_action_type == DSO_YM) { _dso_ym_valid = false; } @@ -727,6 +726,7 @@ void Viewport::leaveEvent(QEvent *) if (_action_type != NO_ACTION) _action_type = NO_ACTION; + measure(); update(); } @@ -767,7 +767,7 @@ void Viewport::measure() boost::shared_ptr dsoSig; if (logicSig = dynamic_pointer_cast(s)) { if (_action_type == NO_ACTION) { - if (logicSig->measure(_view.hover_point(), _cur_sample, _nxt_sample, _thd_sample)) { + if (logicSig->measure(_mouse_point, _cur_sample, _nxt_sample, _thd_sample)) { _measure_type = LOGIC_FREQ; _mm_width = _view.get_ruler()->format_real_time(_nxt_sample - _cur_sample, sample_rate); @@ -1173,7 +1173,7 @@ void Viewport::on_drag_timer() void Viewport::paintTrigTime(QPainter &p) { if (_view.session().get_device()->dev_inst()->mode == LOGIC) { - p.setPen(Trace::dsBack); + p.setPen(Trace::DARK_FORE); p.drawText(this->rect(), Qt::AlignRight | Qt::AlignBottom, "Last Trigger Time: "+_view.trigger_time()); } diff --git a/DSView/pv/widgets/decodergroupbox.cpp b/DSView/pv/widgets/decodergroupbox.cpp index 3b4ec20a..d03082e6 100644 --- a/DSView/pv/widgets/decodergroupbox.cpp +++ b/DSView/pv/widgets/decodergroupbox.cpp @@ -17,52 +17,134 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +extern "C" { +#include +} #include "decodergroupbox.h" +#include "../data/decoderstack.h" +#include "../data/decode/decoder.h" +#include "../data/decode/row.h" #include #include #include #include +#include + +#include #include namespace pv { namespace widgets { -DecoderGroupBox::DecoderGroupBox(QString title, QWidget *parent) : - QWidget(parent), - _layout(new QGridLayout), - _show_hide_button(QIcon(":/icons/shown.png"), QString(), this) +DecoderGroupBox::DecoderGroupBox(boost::shared_ptr &decoder_stack, + boost::shared_ptr &dec, + QWidget *parent) : + QWidget(parent), + _decoder_stack(decoder_stack), + _dec(dec), + _layout(new QGridLayout) { _layout->setContentsMargins(0, 0, 0, 0); - setLayout(_layout); + setLayout(_layout); - _layout->addWidget(new QLabel(QString("

%1

").arg(title)), + _layout->addWidget(new QLabel(QString("

%1

").arg(_dec->decoder()->name)), 0, 0); _layout->setColumnStretch(0, 1); - QHBoxLayout *const toolbar = new QHBoxLayout; - _layout->addLayout(toolbar, 0, 1); + const srd_decoder *const d = _dec->decoder(); + assert(d); + const bool have_probes = (d->channels || d->opt_channels) != 0; + if (!have_probes) { + _del_button = new QPushButton(QIcon(":/icons/del.png"), QString(), this); + _layout->addWidget(_del_button, 0, 1); + connect(_del_button, SIGNAL(clicked()), this, SLOT(on_del_stack())); + } - _show_hide_button.setFlat(true); - //_show_hide_button.setIconSize(QSize(16, 16)); - connect(&_show_hide_button, SIGNAL(clicked()), - this, SIGNAL(show_hide_decoder())); - toolbar->addWidget(&_show_hide_button); + _index = 0; + BOOST_FOREACH(boost::shared_ptr dec, + _decoder_stack->stack()) { + if (dec == _dec) + break; + _index++; + } + _show_button = new QPushButton(QIcon(_dec->shown() ? + ":/icons/shown.png" : + ":/icons/hidden.png"), QString(), this); + _show_button->setProperty("index", -1); + connect(_show_button, SIGNAL(clicked()), + this, SLOT(tog_icon())); + _layout->addWidget(_show_button, 0, 2); + + + // add row show/hide + int index = 0; + const std::map& rows(_decoder_stack->get_rows_gshow()); + for (std::map::const_iterator i = rows.begin(); + i != rows.end(); i++) { + if ((*i).first.decoder() == _dec->decoder()) { + QPushButton *show_button = new QPushButton(QIcon((*i).second ? + ":/icons/shown.png" : + ":/icons/hidden.png"), QString(), this); + show_button->setProperty("index", index); + connect(show_button, SIGNAL(clicked()), this, SLOT(tog_icon())); + _row_show_button.push_back(show_button); + _layout->addWidget(new QLabel((*i).first.title(), this), _row_show_button.size(), 0); + _layout->addWidget(show_button, _row_show_button.size(), 2); + } + index++; + } } void DecoderGroupBox::add_layout(QLayout *layout) { assert(layout); - _layout->addLayout(layout, 1, 0, 1, 2); + _layout->addLayout(layout, _row_show_button.size()+1, 0, 1, 3); } -void DecoderGroupBox::set_decoder_visible(bool visible) +void DecoderGroupBox::tog_icon() { - _show_hide_button.setIcon(QIcon(visible ? - ":/icons/shown.png" : - ":/icons/hidden.png")); + QPushButton *sc = dynamic_cast(sender()); + QVariant id = sc->property("index"); + int index = id.toInt(); + if (index == -1) { + int i = _index; + BOOST_FOREACH(boost::shared_ptr dec, + _decoder_stack->stack()) { + if (i-- == 0) { + dec->show(!dec->shown()); + sc->setIcon(QIcon(dec->shown() ? ":/icons/shown.png" : + ":/icons/hidden.png")); + break; + } + } + } else { + std::map& rows(_decoder_stack->get_rows_gshow()); + for (std::map::const_iterator i = rows.begin(); + i != rows.end(); i++) { + if (index-- == 0) { + _decoder_stack->set_rows_gshow((*i).first, !(*i).second); + //rows[(*i).first] = !(*i).second; + sc->setIcon(QIcon(rows[(*i).first] ? ":/icons/shown.png" : + ":/icons/hidden.png")); + break; + } + } + } +} + +void DecoderGroupBox::on_del_stack() +{ + int i = _index; + BOOST_FOREACH(boost::shared_ptr dec, + _decoder_stack->stack()) { + if (i-- == 0) { + del_stack(dec); + break; + } + } } } // widgets diff --git a/DSView/pv/widgets/decodergroupbox.h b/DSView/pv/widgets/decodergroupbox.h index 07878ae2..c69543d5 100644 --- a/DSView/pv/widgets/decodergroupbox.h +++ b/DSView/pv/widgets/decodergroupbox.h @@ -22,11 +22,19 @@ #define DSVIEW_PV_WIDGETS_DECODERGROUPBOX_H #include - -class QGridLayout; -class QToolBar; +#include +#include +#include namespace pv { + +namespace data{ +class DecoderStack; +namespace decode{ +class Decoder; +} +} + namespace widgets { class DecoderGroupBox : public QWidget @@ -34,18 +42,30 @@ class DecoderGroupBox : public QWidget Q_OBJECT public: - DecoderGroupBox(QString title, QWidget *parent = NULL); + DecoderGroupBox(boost::shared_ptr &decoder_stack, + boost::shared_ptr &dec, + QWidget *parent = NULL); void add_layout(QLayout *layout); - void set_decoder_visible(bool visible); - signals: void show_hide_decoder(); + void show_hide_row(); + void del_stack(boost::shared_ptr &_dec); + +private slots: + void tog_icon(); + void on_del_stack(); private: - QGridLayout *const _layout; - QPushButton _show_hide_button; + boost::shared_ptr &_decoder_stack; + boost::shared_ptr &_dec; + int _index; + + QGridLayout *const _layout; + QPushButton *_del_button; + QPushButton *_show_button; + std::list _row_show_button; }; } // widgets diff --git a/DSView/stylesheet.qss b/DSView/stylesheet.qss index c901688c..5751426e 100644 --- a/DSView/stylesheet.qss +++ b/DSView/stylesheet.qss @@ -261,4 +261,24 @@ QSlider::add-page:horizontal{ border-radius: 4px; } +/* <<< QTableView */ +QHeaderView::section { + background-color: #646464; + padding: 4px; + font-size: 14pt; + border-style: none; + border-bottom: 1px solid #fffff8; + border-right: 1px solid #fffff8; +} + +QHeaderView::section:horizontal +{ + border-top: 1px solid #fffff8; +} + +QHeaderView::section:vertical +{ + border-left: 1px solid #fffff8; +} + /* <<< QDockWidget */ From ced33961c77f7849394a7e3a7ed97059400c2aa7 Mon Sep 17 00:00:00 2001 From: DreamSourceLab Date: Sat, 14 May 2016 10:22:45 +0800 Subject: [PATCH 13/32] Add FFT function @ DSO mode --- DSView/darkstyle/rc/branch_closed-on.png | Bin 0 -> 147 bytes DSView/darkstyle/rc/branch_closed.png | Bin 0 -> 160 bytes DSView/darkstyle/rc/branch_open-on.png | Bin 0 -> 150 bytes DSView/darkstyle/rc/branch_open.png | Bin 0 -> 166 bytes DSView/darkstyle/rc/checkbox_checked.png | Bin 0 -> 492 bytes .../rc/checkbox_checked_disabled.png | Bin 0 -> 491 bytes .../darkstyle/rc/checkbox_checked_focus.png | Bin 0 -> 512 bytes .../darkstyle/rc/checkbox_indeterminate.png | Bin 0 -> 493 bytes .../rc/checkbox_indeterminate_disabled.png | Bin 0 -> 492 bytes .../rc/checkbox_indeterminate_focus.png | Bin 0 -> 514 bytes DSView/darkstyle/rc/checkbox_unchecked.png | Bin 0 -> 464 bytes .../rc/checkbox_unchecked_disabled.png | Bin 0 -> 464 bytes .../darkstyle/rc/checkbox_unchecked_focus.png | Bin 0 -> 483 bytes DSView/darkstyle/rc/close-hover.png | Bin 0 -> 598 bytes DSView/darkstyle/rc/close-pressed.png | Bin 0 -> 598 bytes DSView/darkstyle/rc/close.png | Bin 0 -> 586 bytes DSView/darkstyle/rc/down_arrow.png | Bin 0 -> 165 bytes DSView/darkstyle/rc/down_arrow_disabled.png | Bin 0 -> 166 bytes DSView/darkstyle/rc/left_arrow.png | Bin 0 -> 166 bytes DSView/darkstyle/rc/left_arrow_disabled.png | Bin 0 -> 166 bytes DSView/darkstyle/rc/radio_checked.png | Bin 0 -> 940 bytes .../darkstyle/rc/radio_checked_disabled.png | Bin 0 -> 972 bytes DSView/darkstyle/rc/radio_checked_focus.png | Bin 0 -> 933 bytes DSView/darkstyle/rc/radio_unchecked.png | Bin 0 -> 728 bytes .../darkstyle/rc/radio_unchecked_disabled.png | Bin 0 -> 760 bytes DSView/darkstyle/rc/radio_unchecked_focus.png | Bin 0 -> 724 bytes DSView/darkstyle/rc/right_arrow.png | Bin 0 -> 160 bytes DSView/darkstyle/rc/right_arrow_disabled.png | Bin 0 -> 160 bytes DSView/darkstyle/rc/sizegrip.png | Bin 0 -> 129 bytes DSView/darkstyle/rc/stylesheet-branch-end.png | Bin 0 -> 224 bytes .../darkstyle/rc/stylesheet-branch-more.png | Bin 0 -> 182 bytes DSView/darkstyle/rc/stylesheet-vline.png | Bin 0 -> 239 bytes DSView/darkstyle/rc/transparent.png | Bin 0 -> 195 bytes DSView/darkstyle/rc/undock.png | Bin 0 -> 578 bytes DSView/darkstyle/rc/up_arrow.png | Bin 0 -> 158 bytes DSView/darkstyle/rc/up_arrow_disabled.png | Bin 0 -> 159 bytes DSView/darkstyle/style.qrc | 42 +++++++ DSView/darkstyle/style.qss | 115 ++++++++---------- DSView/icons/Blackman.png | Bin 0 -> 8645 bytes DSView/icons/Flat_top.png | Bin 0 -> 9696 bytes DSView/icons/Hamming.png | Bin 0 -> 8941 bytes DSView/icons/Hann.png | Bin 0 -> 7666 bytes DSView/icons/Rectangle.png | Bin 0 -> 7940 bytes DSView/icons/export.png | Bin 0 -> 942 bytes DSView/icons/fft.png | Bin 0 -> 1411 bytes DSView/icons/math.png | Bin 0 -> 2947 bytes DSView/icons/math_dis.png | Bin 0 -> 2961 bytes DSView/icons/single.png | Bin 0 -> 2438 bytes DSView/icons/single_dis.png | Bin 0 -> 2443 bytes DSView/pv/data/mathstack.cpp | 7 ++ DSView/pv/data/mathstack.h | 11 ++ DSView/pv/dialogs/fftoptions.cpp | 7 ++ DSView/pv/dialogs/fftoptions.h | 11 ++ DSView/pv/view/mathtrace.cpp | 7 ++ DSView/pv/view/mathtrace.h | 11 ++ 55 files changed, 150 insertions(+), 61 deletions(-) create mode 100755 DSView/darkstyle/rc/branch_closed-on.png create mode 100755 DSView/darkstyle/rc/branch_closed.png create mode 100755 DSView/darkstyle/rc/branch_open-on.png create mode 100755 DSView/darkstyle/rc/branch_open.png create mode 100755 DSView/darkstyle/rc/checkbox_checked.png create mode 100755 DSView/darkstyle/rc/checkbox_checked_disabled.png create mode 100755 DSView/darkstyle/rc/checkbox_checked_focus.png create mode 100755 DSView/darkstyle/rc/checkbox_indeterminate.png create mode 100755 DSView/darkstyle/rc/checkbox_indeterminate_disabled.png create mode 100755 DSView/darkstyle/rc/checkbox_indeterminate_focus.png create mode 100755 DSView/darkstyle/rc/checkbox_unchecked.png create mode 100755 DSView/darkstyle/rc/checkbox_unchecked_disabled.png create mode 100755 DSView/darkstyle/rc/checkbox_unchecked_focus.png create mode 100755 DSView/darkstyle/rc/close-hover.png create mode 100755 DSView/darkstyle/rc/close-pressed.png create mode 100755 DSView/darkstyle/rc/close.png create mode 100755 DSView/darkstyle/rc/down_arrow.png create mode 100755 DSView/darkstyle/rc/down_arrow_disabled.png create mode 100755 DSView/darkstyle/rc/left_arrow.png create mode 100755 DSView/darkstyle/rc/left_arrow_disabled.png create mode 100755 DSView/darkstyle/rc/radio_checked.png create mode 100755 DSView/darkstyle/rc/radio_checked_disabled.png create mode 100755 DSView/darkstyle/rc/radio_checked_focus.png create mode 100755 DSView/darkstyle/rc/radio_unchecked.png create mode 100755 DSView/darkstyle/rc/radio_unchecked_disabled.png create mode 100755 DSView/darkstyle/rc/radio_unchecked_focus.png create mode 100755 DSView/darkstyle/rc/right_arrow.png create mode 100755 DSView/darkstyle/rc/right_arrow_disabled.png create mode 100755 DSView/darkstyle/rc/sizegrip.png create mode 100755 DSView/darkstyle/rc/stylesheet-branch-end.png create mode 100755 DSView/darkstyle/rc/stylesheet-branch-more.png create mode 100755 DSView/darkstyle/rc/stylesheet-vline.png create mode 100755 DSView/darkstyle/rc/transparent.png create mode 100755 DSView/darkstyle/rc/undock.png create mode 100755 DSView/darkstyle/rc/up_arrow.png create mode 100755 DSView/darkstyle/rc/up_arrow_disabled.png create mode 100755 DSView/darkstyle/style.qrc create mode 100755 DSView/icons/Blackman.png create mode 100755 DSView/icons/Flat_top.png create mode 100755 DSView/icons/Hamming.png create mode 100755 DSView/icons/Hann.png create mode 100755 DSView/icons/Rectangle.png create mode 100755 DSView/icons/export.png create mode 100755 DSView/icons/fft.png create mode 100755 DSView/icons/math.png create mode 100755 DSView/icons/math_dis.png create mode 100755 DSView/icons/single.png create mode 100755 DSView/icons/single_dis.png create mode 100644 DSView/pv/data/mathstack.cpp create mode 100644 DSView/pv/data/mathstack.h create mode 100644 DSView/pv/dialogs/fftoptions.cpp create mode 100644 DSView/pv/dialogs/fftoptions.h create mode 100644 DSView/pv/view/mathtrace.cpp create mode 100644 DSView/pv/view/mathtrace.h diff --git a/DSView/darkstyle/rc/branch_closed-on.png b/DSView/darkstyle/rc/branch_closed-on.png new file mode 100755 index 0000000000000000000000000000000000000000..d081e9b3b90d774450a8ea48f1184019e33a755a GIT binary patch literal 147 zcmeAS@N?(olHy`uVBq!ia0vp^Y(UJ(!2%?APo63Uq?nSt-CYAmd+F5V%0wNv=peG!PC{xWt~$(69A|)Be?(o literal 0 HcmV?d00001 diff --git a/DSView/darkstyle/rc/branch_closed.png b/DSView/darkstyle/rc/branch_closed.png new file mode 100755 index 0000000000000000000000000000000000000000..d652159a365396a046329cfc7695c89ee54431ca GIT binary patch literal 160 zcmeAS@N?(olHy`uVBq!ia0vp^Y(UJ(!2%?APo63Uq!^2X+?^QKos)S90ZA8lL>4nJ za0`PlBg3pY5H=O_B-6{JiOAS{|sjWh15M=978y+r}k{*WnkcFe(-;BX~D)* rS0)5haD*y~YzrxP=F!`JhM)Jr2M#8aN7~DQS{OWC{an^LB{Ts5zf35P literal 0 HcmV?d00001 diff --git a/DSView/darkstyle/rc/branch_open.png b/DSView/darkstyle/rc/branch_open.png new file mode 100755 index 0000000000000000000000000000000000000000..66f8e1ac619d242f3d5a31ffb11291c09ea40468 GIT binary patch literal 166 zcmeAS@N?(olHy`uVBq!ia0vp^oIuRR!2%?ApR4f$QjEnx?oJHr&dIz4a+s35-CY>w z1e^Sc1@brxJR*x37`TN&n2}-D90{Nxdx@v7EBhS|ac*foG2iHXppcfQi(?4K_2h&D z3_UzN#dZqZOE?US3>3JRaTpjFIL#0^D8eWr9I$mUBg3(s>=R~}U&3=E8!o-U3d z5v^~hTl*b$5NO-KaZ$^2My7CvC3jk<^u2WvDUf$c)s+rzv%V1E9{A4TMP-Z4lZ6F6 zJvyqaW~@Kfmu*$9pTBmh4fB87Nq1MQib$?;e-iXiZ<}* z4@CWD;9;1v<$&N$mS=`@#Ch2ZV#{}RJvQ4QaCNnQ-mU-(MmI+R8*yI$%dfxAYY;s7 z_m>{S8>aWE2aPM4zg_apbyj4!CUG}xvu2#YiDkvbx1hxrj-4fv9WXW}Qu2zs*=;EB~B@C9lD4Pt3jc#EK9uo4$63*ah<+ zss)BhMo2G`V&I;1@X1nvU9S{R&b}_{bpE^krGqN=vm#%w`~A&$lHbmbGYy8TuE%Y! g4!>ah-@}e^jYL&g^Ys^bz?fn1boFyt=akR{0GqMPK>z>% literal 0 HcmV?d00001 diff --git a/DSView/darkstyle/rc/checkbox_checked_disabled.png b/DSView/darkstyle/rc/checkbox_checked_disabled.png new file mode 100755 index 0000000000000000000000000000000000000000..cb63cc2fac47ad304451f864be5fb9b9085910ee GIT binary patch literal 491 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdzmSQK*5Dp-y;YjHK@;M7UB8!3Q zuY)k7lg8`{prB-lYeY$Kep*R+Vo@qXd3m{BW?pu2a$-TMUVc&f>~}U&3=E7Jo-U3d z5v^~h`}!SnkZ7B~UO?mu^MY^z;cfGr%T7e}s1?W?8ZIpHm|nqi{YV9S;d_3jiRW9V zC@j#CiOINs*XRD1%4NQnC4ao1_;X|a8O{J^t^>=I&uJH^EA=Yy=q@QJXMe6{= z3nu?QmTiqn8yL4lU7L4ID46lSggggJ-lhEfrK>ewSvD~j#OXbEpLgByNx%V?Yugz2 z%=F`x=yC0fIn20j(F*mKC7cqgJZ$I97rnv0D^bF3N854ErU!Bp^;Wf}GDU{OUt^u0 zW>d#j^wD7Ns`t{1S6kg*n)>$7^lzH?%EjxII~|lJaHueWsWo@_*Zi9Q-(<7@f_{&8 zrO0b9HEPU+9v)SBw)%l|Fntmmjz{W9#jS ei>v=l+`}wZn5FJg^3NX_F$|urelF{r5}E*p0K`TB literal 0 HcmV?d00001 diff --git a/DSView/darkstyle/rc/checkbox_checked_focus.png b/DSView/darkstyle/rc/checkbox_checked_focus.png new file mode 100755 index 0000000000000000000000000000000000000000..3cf0e54059775600a8e836deedb6554ad585602d GIT binary patch literal 512 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdzmSQK*5Dp-y;YjHK@;M7UB8!3Q zuY)k7lg8`{prB-lYeY$Kep*R+Vo@qXd3m{BW?pu2a$-TMUVc&f>~}U&3=E95o-U3d z5v^~hTl+BuinQ%-*02*_kmu;@QQ-Ph!-_?_db92FFvpMeB=MVEo(xKo_I3NC6zZ(=0&OIbJ3?_Dc8N+ z`UNkRF^0~(c}{-iCWbwnBBHfL%CQq1TMY!5EVl?<6pdb_n6PC1bXH4-y)I3~VM;~s z-FEcO)Z|&b)i}Xx>d_pnIwpaPh*fJC^JCvPMs7dMxW?tlzPPpL7)1rIz5Uz9aA=0Z z$0X%%56X6&eY&iWU8M128RLuKijP`*I@1^2k7JqZH~ZxrtGO>*ruKL*uUZiN(fk8z z4P*QPaR{|hzTnsSdauviQq3^Yy%*$?9v7vi2H2P(8128Bb|(G4PNvN{@ZTf(3sn_sOi&WJR5%589C1Db zhCs&)0xKfAB%rDi8*{57O<)4_5qWijS3LmgKot?w^!;`wQO?#_V**LxkEL?C;d$Q3 z34D<_j%&$$-a|FC);<7TfKs-gBytH%5;%7a2k<;^G*6NSFcsCSFK#waI05=$7(Tm( zXKA)Hc7gXz!E<>7_ErgOC56D&1`2f;J-djcl1whQ1*)tmxvW08H=aDaB2-ihxwfOi!+%lfAUc7T48B=@f2S)wSqMWl~v zZI(b4)#rJ^CmD~QfYn(73rQ)kZ~?z)ATUOY#(t96+Wf5x{DKuI1xkTZpzt0jI=8N~ z@D(TpewDxrBCCa!&WPR`@bXPS;RdLmG@G0AVz*kYZB*;P;G2LaatSOCE0wD-47cV1 zuZZ-3B^9~I`tuKy4iEtEUw^L;PUs>VUo&4)=p@!2lYf|GrJ*qgDsqPEE>LwM@VVy$ iRL|pXH+A&QKavmPo@ys59Ed6a0000i!=8^7%l^QFXfZK^@uS4j^DKMqVLM}aV4B)FR`rf|z!UHUJOS5t!0q0ePRBdo3H&O7iZuLG+L(Tz(n3IQ6(aHMb*NqB$;`zt z5D~a>3aD=v2%sNl)0?@WcEEe+ItBbS(H*J*S9_pKz#C8keK3QULB0kPzrtQF z6~}U&3=E9Eo-U3d z5v^~hTl*b$5Mg-k8hGeCL!&lB&^xD7y>BB#3gn$qXZC6ymd#kPSfgCwOO2D=r^N+5 zJw7|dgetV6qbk!>#or&Sckj|)rDfLlx9!BzLz){NpZKh3z}$B1``V5SO@~m`&d1TB zFPXg)nog`1-usI20_&}gBemzbKX;_n9XqyBf99W!7jwS#7#6F4()FBFa_MYZPgcU( zS_U_U-aih`PlO_t6}*e$OX!)Ie`yQ*8keuHPS3X2n8Pqxfn#3m>Ec5MHB4$L|F3^% zND!}B?-loe_r`C{2jMH2Uo}`Q;Db^cY&se~TB4iwmri9$b1?Uu;sB%_cFATAov~jhym_YEdgAln?ixeQ z^=a2;y}eWCW~RA2fzc;$?>Dw)YxnzqlxvtK94R$eY;o}lF!UKbUHx3vIVCg!09=m0 AT>t<8 literal 0 HcmV?d00001 diff --git a/DSView/darkstyle/rc/checkbox_unchecked_disabled.png b/DSView/darkstyle/rc/checkbox_unchecked_disabled.png new file mode 100755 index 0000000000000000000000000000000000000000..ade721e81ba47fa792d4586516b8744f8c49c8bb GIT binary patch literal 464 zcmV;>0WbcEP)z3Xli7-6*p9aNvlr>Itd;TFIVxd-y|OyrCsEBb@QA; zCyb(7HXkO*sBg@biZoF@2MSgIU*CL)>Rq?ji!Gh`NAd|iaAZN1hu>)c0000utO4E zgALe-72aZrdoVd*6iH49dk}js2nb`Lhd`h_#6-{6{ZstkQ_jGDkMzA%C6amA7L^2o zADFbM=L4k*k~Q{2mx$;fKvmX`OBbqa0u2FE(N{a#1({I@03MKYBN^JU5M4kdeIFO( z477-h#f)UIW=FY|Xr}B#+j(F`m90W*=?cKrRjMR_hWpdCZg)9=kqp*4tyTKws0;$sbqaXU_RZ>1VQv)VWFU8GbZ8()Nlj2>E@cM*00Fv5L_t(|+U=WLk^&(N zMNQT>E=U)k3)21RpJh4PJd%48X!1Rk<{TbF5DteGD^~0|g!!@C>GY`iyW{mLQwXR9 zjuawUp;Cior+G8=rW1YKL<346a~6o?i6 z?{azNxS!7yQSaX@eYYe*y&Lw~I5fO96fyKs5_ zy6%Wq@1q*&nfKd*=(Y$VMgp?uA^!zDB{1N>P63ZikG1hg_|Ito*$P`m0;}iYwLp&q zHaxr-YLO5D|8EUI6+I<5HvIcKKq-fN*91m!MLQ2@g<(~e)B}-PC>A_jKn)xdK16_J zA27UtS~#Wz%?8hwpwaM*^Mp_VHSm!VAk|=R&oeeflxnaSCSoLLra2!WK{LTeNN_t8 zMa*IEe9%Kt!~(_`37Qq2CE;g++G)V5=dIuQ^&@G>nP7%|MFEzZSE$DJ=G|h^>^+ap zZ@wUrJxU{aU6H9xwr+_{8tbBDYw}%RmCY=Ic3C>3gpljWFU8GbZ8()Nlj2>E@cM*00Fv5L_t(|+U=WLk^&(N zMNJl_8_?B`8_ zy6%Wq@1q*&nfKd*=(Y$VMgp?uA^!zDB{1N>P63ZikG1hg_|Ito*$P`m0;}iYwLp&q zHaxr-YLO5D|8EUI6+I<5HvIcKKq-fN*91m!MLQ2@g<(~e)B}-PC>A_jKn)xdK16_J zA27UtS~#Wz%?8hwpwaM*^Mp_VHSm!VAk|=R&oeeflxnaSCSoLLra2!WK{LTeNN_t8 zMa*IEe9%Kt!~(_`37Qq2CE;g++G)V5=dIuQ^&@G>nP7%|MFEzZSE$DJ=G|h^>^+ap zZ@wUrJxU{aU6H9xwr+_{8tbBDYw}%RmCY=Ic3C>3gpljWFU8GbZ8()Nlj2>E@cM*00FK^L_t(|+U=W3lEWYj zMI&9@_Q}(`u2cpl>3<1~)?G61A!s1*ckDcfFZ2UV}LO?5Uq!7sp&5|Lr zfTKjnjPRZkWJYjL5i&cxRf5b8ZWSRb!H-Cg)!=(2=w1ph*!w+IAXfO}IR0~7*F_Qa z`pw#RYZBDEVegGY!&^fUrH_&@lDEd7sB!Nt!IH1UVjcpe@#iI>l*c;nh*$5U8tIw$ z+l1&g2_h;1+4GS90-h2W@Lz|3$D+sDcqIJ$Fn}zDEtSCPdH7ynL;@Qgz8UI~5CQ)& z2cR}RH8?i>>pVc&4v)?WjLjA8JfIhbwX>uih_phn;OPQd;F$0s0xau*=>@dHF(qg= zc(w$MhG(27gbHYZkCXt}4UYCaV^Kue4UWP@RDxz2^AQp>6MTdOw?k3H81~KwJrqSu zV2n!8tne%eQYe>aG`#0`e*H)qaweD|UsZtR<`t^3qj^g#n!V@I`OOz3vPZ3w_Z69r zWc!xbim@+B4kW+tRoToUXqTlkN(i|wpGAW8(Zqf-18CSiA59@;O!aKeLkRQXw z1e^Sc1@brxJR*x37`TN&n2}-D90{Nxdx@v7EBhS|ac(A-+!@lDKp{;}7sn8e>&XcR z7pulY;Wn^IBG(*7A%~?b^VC!N=hHC=sm-IdEdjT~uc)I$ztaD0e F0ssj2CNKa1 literal 0 HcmV?d00001 diff --git a/DSView/darkstyle/rc/down_arrow_disabled.png b/DSView/darkstyle/rc/down_arrow_disabled.png new file mode 100755 index 0000000000000000000000000000000000000000..5805d9842bb3c8bdf9ae741ebabc690a4929585a GIT binary patch literal 166 zcmeAS@N?(olHy`uVBq!ia0vp^oIuRR!2%?ApR4f$QjEnx?oJHr&dIz4a+s35-CY>| zxA&jf59DzcctjR6FmMZlFeAgPITAoY_7YEDSN1y`;vAyZcdU741BJ9aT^vI=t|uoP zVCdoDDYjGKUczBuWT3#kjKjddz-flSK@mm~;ef4+85xf4WSF7(8A5T-G@y GGywodVJqnX literal 0 HcmV?d00001 diff --git a/DSView/darkstyle/rc/left_arrow_disabled.png b/DSView/darkstyle/rc/left_arrow_disabled.png new file mode 100755 index 0000000000000000000000000000000000000000..f5b9af8a34edb5f8dd767bf6afa303b89a31d38f GIT binary patch literal 166 zcmeAS@N?(olHy`uVBq!ia0vp^Y(UJ(!2%?APo63Uq!^2X+?^QKos)S9bR`<_$&i(E==bj5a^zeU&bwdtch>=_qAH5C#I(A02SZ= z-pqyvve{!vh3V|K2(Z;?j0;|Hq>tJ+4>OtTGCRJHZF@ZOcP8^q5Cr`I<~ScB9o=p= z&m^6bC_rXoz*@WDo;`Hvf?xzGIc?G2OIwY`1pwD|f5YrH(7gbIoJl|JS(+dUbvmTb zB5r&Bqu`aU1z`4E`<=%)mdf9?a};sgxR2RT5LuX#bWVMU#oQ*xD``&)6vecw1(rnna}5U zBv}}JUU#-zt!rHi$mjDrIL<6^D(>w}Oik4Xve{RW-j%cpG=ZjI)y&=*IB?)K*LCj$ zRDHh$oRrzj^z?Kq>70&LUiJM2-~*uKdfs9uz9g&qe#y)}0Tx})E2Z%}7S+9tjgq8G zz(UpczpGZOFDG$V%H`Y_mC6rh_9@YK{BbA$PyEhTe19x7TSPh3(2no@8jnJ}d` zxvz`)Xu-@3Q@U%O-8ttu@B6#&eeXFJcD1YLUFyZ`x^CY1#GHEIZf^VJ)fa|)EWFnC_vkwH{ z6N%(XrPA_FUeEIm3uZ8T)68avQmNeZ^mN!MezY4vBl-Klxl+0Oc_(iW1WUkMr#lwA61mWP^6gmcikThUVXxDm7!9M*?_AgI18~gF z1H*|#;zYYVZ2=^WVYb|CxN5a}7PC=xsG2|quMNcGX92#vbZHT|j#=*jKpN?%*2wG> z^-_116auGOL$e=c_Il3%1~2LHcaC7B3vd(Eku=gAhHwvQuxEgJEbHl{+%bXUhz^zO z1aMzw`&uyYd#6AxNnr@rTl>rgF}wdX0Kb}a^ohVmh_EiNtnd~J1gWB9;< zQeQOsw@G^iF9MGY79E(`tkvF|pP&B+z|Uqc0A~cJFP2I-+U4om@B3+mVcy`Fr2H#G zLmzjg?Ph4o^Sm*c%>u*JKeMaA-Ny3E)PG1ufSZlmvu)!&6#&$4$V4J>LSZbzKz@!f0oC0-WJTPn`cd7ULmjdwV_3N>JR^Ch@Mu49r zCVw66Td3zdkjbBj1)9P*4P3cX>YXHML93IjD=*_uduhM&erA5G6hBV=)%fnc3m~BEWl@{B4tt ztd`<#qlM`-0yvyl9|GPAWS6&QUfFbRVmcmrXh+{#yQ{Lvk8ZNi2H*ttC2;gmqI|Yd zoQ445R%2mn!Idkk7eHNTm$C^1d#C+i`2s+0wCA40l0kF;P`9C9YGz>P!&L7sK>(cJ zvJHO5IQaqqye_O*vrO)P(_UW&4#?dBw8Hj*6$t<=+DTbAfSD0yB{IN60y=A!tk7x+ zB$l>qbpQ{e1Bl-Qr?x%yZL1|Pvuis(4FdVd0A`jowRK7YIx@0%MLSOM_T z{v9eyV!0jJMQ5GH-hRa(IFHQ_b9)Z?mKvFOmNmV*@!=&7gwT^7eV48y+tYIRCxlLiT(zip~u_E}K!eZQ1UCJ)XWpcn*qFv|mj2Hymm zGOGZ1&1OGlqk`+ej=|zgCiDDE0Kzalw_mH}fqMq8tl7*>&(3Zg z<=pb}a>SKCQhxe=1 zDZv#_S~^K`0Q0>2NJI5nZ6eAi5Q&W)Tj`Fao}46Y)*j1A!XehJuV&-U1oWX;y{+O9Vm8?gbB$>af^ zw*ZW{XL~Faoy!4ZGMk^9n`^`d07#`$FMw5G<+u=%tPliCX7&)sr_<@k@^=LQ*e#cH zlGcEH(Ye>fV(~_l?|Kj7Dkc) literal 0 HcmV?d00001 diff --git a/DSView/darkstyle/rc/radio_unchecked_disabled.png b/DSView/darkstyle/rc/radio_unchecked_disabled.png new file mode 100755 index 0000000000000000000000000000000000000000..6ece890e750b0685bbd818f22e5fbf999ccd35e1 GIT binary patch literal 760 zcmV6Hyq3pRX0tGI#@4O=(z(ra$opASN0&l!V!~ zGsFupMi4Yn7A{E9xN<|Zbl8%Z;>x%YH!O_WHWI)PKm&#gz*M7gJ{PpnM5bj5JNs=u!`ga zdVmk2x~L*MwQ&UK2QB~`Dl%3m6rLT6fN7e+lZKH~)f)mY2nNUV`F!P|tYujVfhklk ztLk(#78{?OoOB!Qf1;T{t^7JLns=NBjk1M8p$uFZv8+*G>eJ>Xz*wU-!A3(nlNkmc z35?D<&ckN1bxP&(QZkYFE+Y35$z-KmF0XlIJs*K-nw{NUU8}&yS;x70*z&)zv)PwG z&(~_T&+A|YycTzd!$Uw!g29Pa$^inZw}4*5Fa|v{UIP(HqgwGgaEIaP+*}D*M%BLn zkV52jtL1e>^_8ez^ev!pO8&g(E#tx$L?XTg094i2O?hBJARzl5{sneL_4H8%R5gO? zZYu(BRiyu@0>FU48{Yx~Mc}+;S&3GgZQD)(=Mi(Ony70 zgw)FKi^z1|aUM4cH~kt$W3lniTU)BCIV+pJ6jU9r-EjY4+jdG^H>WTlBDu5C=s2S%SwytG|&qy1B<|RZG49PACf*`UEpTkaXb%RO9HU}g)|IfP+T{SND8Rm6FUm; qM77uv3N6gc%>4ATZ<{v%H@^W&H{IQg@q%gq0000YQ` z_uPBWJ)m8c*^W17=e(1L&RVkWoSZjh=e!d;=Vi`|J+lJUvcDE2>BeJo^#W9K zy=jzJ2ouYbgLz|sNV?d1?dri-LBF~2dyh=aPxZhlEQ)H+&+?T>(8aWZk~_nP6TmHB zWu_5x0LzzWKyOF*U@R~uXrc1d*bKUG^~g;OY^bs&U=9Ep74&1H=BE<)fY9Fn0M`{M zT@*lJm%k0m2mr*aQ`R&P5g@P^oCUClPxnOu6zu}`%mI{73d0u#07igV9xQY9L<%fFafwWq7xbPBT8dwyy zQf^{kqgJTxtVXM49}ywr{)#<6?>jaaNW5{BEYPdUmIzA--vLl=Pb=8xK17S1~JrkZN%U;P2o9NB+fxiifG0000~P literal 0 HcmV?d00001 diff --git a/DSView/darkstyle/rc/right_arrow.png b/DSView/darkstyle/rc/right_arrow.png new file mode 100755 index 0000000000000000000000000000000000000000..9b0a4e6a7a8097818d9c0626c84f19f4d690dd31 GIT binary patch literal 160 zcmeAS@N?(olHy`uVBq!ia0vp^Y(UJ(!2%?APo63Uq!^2X+?^QKos)S9wUkJ;l%oZHT?}(3D>Wp7T%b9XV|~Y(T_!;F44$rjF6*2UngIS-C?Eg; literal 0 HcmV?d00001 diff --git a/DSView/darkstyle/rc/sizegrip.png b/DSView/darkstyle/rc/sizegrip.png new file mode 100755 index 0000000000000000000000000000000000000000..350583aaac4aa474ac449eaea2cc7ddd060276b9 GIT binary patch literal 129 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!D3?x-;bCrM;TYyi9E0A8dZe4lyHC-T!u_VYZ zn8D%MjWi%f)6>Nz(!sM1rC-2ha+zM<2rMwpeI*@Z@PO%TWH}e*?iSqXK(y9 XcW6R37#&FAr-gY z-rUH`puoZ4SQyZj9Qd}kRkgExspwA+*PdmovgYQ`l$1@M%Pi(EdF8VmvF&CX@A%e}M=bpY`_UHx3vIVCg!0H#+y$^ZZW literal 0 HcmV?d00001 diff --git a/DSView/darkstyle/rc/stylesheet-branch-more.png b/DSView/darkstyle/rc/stylesheet-branch-more.png new file mode 100755 index 0000000000000000000000000000000000000000..62711409d7ed69ec98979394795822630458d9eb GIT binary patch literal 182 zcmeAS@N?(olHy`uVBq!ia0vp^5PiX%b9eR9<JS%C8jVk7;fc! UBk#RM6lem2r>mdKI;Vst0ANBkrT_o{ literal 0 HcmV?d00001 diff --git a/DSView/darkstyle/rc/stylesheet-vline.png b/DSView/darkstyle/rc/stylesheet-vline.png new file mode 100755 index 0000000000000000000000000000000000000000..87536cce16aabb3710663f720f8d354b1bb0b757 GIT binary patch literal 239 zcmeAS@N?(olHy`uVBq!ia0vp^fk14@;zM~Ln>~) zy|9s&!GMF=@x%h2gO1`OFspnaH4_oY}#FfpL8m Q-wTkir>mdKI;Vst0J6j{!2kdN literal 0 HcmV?d00001 diff --git a/DSView/darkstyle/rc/undock.png b/DSView/darkstyle/rc/undock.png new file mode 100755 index 0000000000000000000000000000000000000000..88691d779507c9b809391396407f5cb4a6497c40 GIT binary patch literal 578 zcmV-I0=@l-P)WFU8GbZ8()Nlj2>E@cM*00E{+L_t(|+U=X$4#OY} zLz`&--S*43w`rPoNlWaQLS8pfd~hg*uq-oX%osV0`LJ!+SPR{}9r(JUC& zi*OVO>rs3r1nW_FCJ5_Yd@BU&U3e=9yOQ`b5bSE=k3zUrc5+?UXD9c4F9B>-qyH)% z1tH=BQxRVU!7FWl=67leWRLz4ahXo| zoe{&T7oZ-FMxDSsBBvjZ{}acq6e%f?_~rzJ_LzyflS`(bcuN3?R&llQTw-2U8((=NC3B QV*mgE07*qoM6N<$f{lRZzyJUM literal 0 HcmV?d00001 diff --git a/DSView/darkstyle/rc/up_arrow.png b/DSView/darkstyle/rc/up_arrow.png new file mode 100755 index 0000000000000000000000000000000000000000..abcc7245212f19a5dbff1bb19647b1dd4bb05b6a GIT binary patch literal 158 zcmeAS@N?(olHy`uVBq!ia0vp^oIuRR!2%?ApR4f$QjEnx?oJHr&dIz4a+s35-CY>| zxA&jf59DzcctjR6FmMZlFeAgPITAoY_7YEDSN1y`;vAy| zxA&jf59DzcctjR6FmMZlFeAgPITAoY_7YEDSN1y`;v6FKKb3EC1BH}5T^vI=t|uoP z;C)upuu) + + rc/up_arrow_disabled.png + rc/stylesheet-branch-end.png + rc/branch_closed-on.png + rc/stylesheet-vline.png + rc/branch_closed.png + rc/branch_open-on.png + rc/transparent.png + rc/right_arrow_disabled.png + rc/sizegrip.png + rc/close.png + rc/close-hover.png + rc/close-pressed.png + rc/down_arrow.png + rc/left_arrow.png + rc/stylesheet-branch-more.png + rc/up_arrow.png + rc/right_arrow.png + rc/left_arrow_disabled.png + rc/branch_open.png + rc/down_arrow_disabled.png + rc/undock.png + rc/checkbox_checked_disabled.png + rc/checkbox_checked_focus.png + rc/checkbox_checked.png + rc/checkbox_indeterminate.png + rc/checkbox_indeterminate_focus.png + rc/checkbox_unchecked_disabled.png + rc/checkbox_unchecked_focus.png + rc/checkbox_unchecked.png + rc/radio_checked_disabled.png + rc/radio_checked_focus.png + rc/radio_checked.png + rc/radio_unchecked_disabled.png + rc/radio_unchecked_focus.png + rc/radio_unchecked.png + + + style.qss + + diff --git a/DSView/darkstyle/style.qss b/DSView/darkstyle/style.qss index baa433ed..9cca2c81 100755 --- a/DSView/darkstyle/style.qss +++ b/DSView/darkstyle/style.qss @@ -245,18 +245,13 @@ QMenu { border: 1px solid #3A3939; color: silver; - margin: 2px; -} - -QMenu::icon -{ - margin: 5px; + margin: 0px; } QMenu::item { padding: 5px 30px 5px 30px; - margin-left: 5px; + margin-left: 2px; border: 1px solid transparent; /* reserve space for selection border */ } @@ -525,21 +520,6 @@ QSizeGrip { } -QMainWindow::separator -{ - background-color: QLinearGradient( x1: 0, y1: 0, x2: 0, y2: 1, - stop: 0.0 #302F2F, - stop: 0.4 #333333, - stop: 0.5 #404040, - stop: 0.6 #333333, - stop: 1 #302F2F); - color: white; - padding-left: 0px; - spacing: 0px; - width: 3px; - border: 0px solid #202020; -} - QMenu::separator { height: 1px; @@ -574,19 +554,6 @@ QToolBar { font-weight: bold; } -QToolBar::handle:horizontal { - image: url(:/qss_icons/rc/Hmovetoolbar.png); -} -QToolBar::handle:vertical { - image: url(:/qss_icons/rc/Vmovetoolbar.png); -} -QToolBar::separator:horizontal { - image: url(:/qss_icons/rc/Hsepartoolbar.png); -} -QToolBar::separator:vertical { - image: url(:/qss_icons/rc/Vsepartoolbars.png); -} - QPushButton { color: silver; @@ -1180,39 +1147,65 @@ QStatusBar::item { border-radius: 2px; } - QFrame[height="3"], QFrame[width="3"] { background-color: #444; } - -QSplitter::handle { - border: 0px dashed #3A3939; -} - -QSplitter::handle:horizontal { - background-color: QLinearGradient( x1: 0, y1: 0, x2: 0, y2: 1, - stop: 0.0 #302F2F, - stop: 0.4 #333333, - stop: 0.5 #404040, - stop: 0.6 #333333, - stop: 1 #302F2F); - width: 3px; -} - -QSplitter::handle:vertical { - background-color: QLinearGradient( x1: 0, y1: 0, x2: 1, y2: 0, - stop: 0.0 #302F2F, - stop: 0.4 #333333, - stop: 0.5 #404040, - stop: 0.6 #333333, - stop: 1 #302F2F); - height: 3px; -} - QAbstractScrollArea { border-radius: 2px; border: 0px transparent #3A3939; background-color: #302F2F; } + +QSplitter::handle:horizontal, +QMainWindow::separator +{ + background-color: QLinearGradient( x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0.0 #302F2F, + stop: 0.4 #333333, + stop: 0.5 #404040, + stop: 0.6 #333333, + stop: 1 #302F2F); + color: white; + padding-left: 0px; + spacing: 0px; + width: 3px; + border: 0px solid #202020; +} + +QSplitter::handle:horizontal:hover, +QMainWindow::separator:hover +{ + background-color: QLinearGradient( x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0.0 #302F2F, + stop: 0.1 #333333, + stop: 0.5 #404040, + stop: 0.9 #333333, + stop: 1 #302F2F); + color: white; + padding-left: 0px; + spacing: 0px; + width: 3px; + border: 0px solid #202020; +} + +QSplitter::handle:vertical { + background-color: QLinearGradient( x1: 0, y1: 0, x2: 1, y2: 0, + stop: 0.0 #302F2F, + stop: 0.3 #505050, + stop: 0.5 #606060, + stop: 0.7 #505050, + stop: 1 #302F2F); + height: 3px; +} + +QSplitter::handle:vertical:hover { + background-color: QLinearGradient( x1: 0, y1: 0, x2: 1, y2: 0, + stop: 0.0 #302F2F, + stop: 0.1 #505050, + stop: 0.5 #606060, + stop: 0.8 #505050, + stop: 1 #302F2F); + height: 3px; +} diff --git a/DSView/icons/Blackman.png b/DSView/icons/Blackman.png new file mode 100755 index 0000000000000000000000000000000000000000..b3a268e5d73eba03aa2796714128937546a15702 GIT binary patch literal 8645 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�v{ct+;P-b|1DL&jh4^yE7nAn%Lgm0^H{Pc%(PD^84L`^Xk=}* zj6)1VazV5~>9WNWXEZOsc;Id@m)CAZ_EwKZL-4U6RzlSmWR9RSQx&7u2k>rVn`MD@ z2a9gR-;J|Fy6aafA1WYC` z^}Y^iGH|5{d!!%*N$#%GBoy(Yngaouz&!##|_m?{lmQXkxqo**V5+eb}6* zzc5lM@F?cu^~>M(#x7uTG5pCsZn2o0HjtG~bJt$HwMzK&#*<5}I}cZlV9+ttrIx4> zt-w-)qN!84(I!aNQY>|uPf6!MAdlxc<)qD4YxJiIkYE{~?D3)M$-c_^ayatU}`7476ZY>GJ zI)Xa`Ln(1+xa{wrPqJqy`q!>qu3duh4?FsN7W3`5lROGb00`-8e(&=3oteO@%jUS` znSOD9jq*x+%9vv7F3uZ5kvo%OxN4;LonfQ;SmZG|*3@?^b7}4D?nenMY3U(z%e;%J zsib>Ma9me*t@e*i;`Yzrc*JaE^de|InLPRnlVRAr5c|L*hk-8Q%^ZjrJAOO(NN`XU zkn5`DvV8~4_Lj5mGdxD0eWrQGFK5_Tm6u>3hGS}xaO7b_cW*iH>Z4y;oQV`STN;N1 zRA$zLRoNe-aW0r4LY244={cGyiBF||jYZ?ClY);ziK6}kzNp`HcdWP{5;tbqUgu@mJ*XV^T}g^u&gg&HKGX=_ zt_VNQ#AJQR1|QWgz!7O-Un@XN1k#K?q8+B*Go6%jD6Hn$?+$)?e<^LUNguTC?f6Qn#pCm?WUoQB5I<8PRxrFCBoH;Z^9O9M_jwAfx;$9tJ*s2C{$DRZk^nR=f)9__F8Cs*eUx3%7>oDE z2Hy`(qP-_hvv0TqLtu;dyrZ4_&M_3cJbrG7Yc%)1pBOHQyUaQih|9(9Ot81*#c%RF zUu>+mfIQZhB8KvukUV;GM;krUWfz8v5RoEGh;X|A0EEL5;DUH>*V0 z1DXx==j1Z0r|dF>SfFAe8iL^7s6b~$gVLZ$$V(y0YzZO;#nltTf}+XnLUdG}U_-O! zb{u%gZgdnD9sYtfURh{f^;XI_&VhNvEvtV)*hl?7bz5pWX;jkVXzFhG)GyrtZO(qJ1hN}-=zhRN`?g^*>@oMN&BHSe_#Hib@X?ei@%9H0TAp^mW>*>(yO@kJ5#C$$2R}3h2j5qpr~;3xB$_b(6BKWLiJ?;Dhe9% Jm2&36{{ty7N^<}J literal 0 HcmV?d00001 diff --git a/DSView/icons/Flat_top.png b/DSView/icons/Flat_top.png new file mode 100755 index 0000000000000000000000000000000000000000..5609318408fd18fe38de23bc84a7b5303d3ad9be GIT binary patch literal 9696 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 PvIS6*Q-xK^n0)>pTx3;J literal 0 HcmV?d00001 diff --git a/DSView/icons/Hamming.png b/DSView/icons/Hamming.png new file mode 100755 index 0000000000000000000000000000000000000000..c9160ea00b842092d4e5133d773f0b856776a29d GIT binary patch literal 8941 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 literal 0 HcmV?d00001 diff --git a/DSView/icons/Rectangle.png b/DSView/icons/Rectangle.png new file mode 100755 index 0000000000000000000000000000000000000000..cacd471152c5018c2c3d03e8daa75f4150106d0f GIT binary patch literal 7940 zcmaL6cQ~9+6feHZ>Lr#$35iuhh~A@H(Mu3Dx)5EWMPDVXUZayX6fpmqO==1*^Naz1lxMyr zX_da($)KP6v|Le+8JkQA*kIu>R^c9F(kz&U#ib*>Z%5I?%yR#=0p8e=uz8>cs$2n9 zIinNjEcw8aNkdIl*nk)QP=yL=j$)PoGFeSqkoDWxOvDlXuNp+aO!m^MXl&@jD$a*ofc^0RIAxE%wJ>mCIRNHhzQ#ANR3?CwGTQ=pD=Q1#<0!-ZUjJ}oZ-w5g|N8M?2KN((J*Z8WdbWwhnuNdfYu|m~Uf6jhQKPQ~M96i9)E#2oI28a}9Rh9|~@8;(8-hSwb)oV(w+srlcZ|(Hi zrz0H3?u1L?`Ow5Yxvf>!yH4}i@eTY>1mWk?a(y;WZ?q*gUE*+iZN!qKx5fz}``Aa8 zV_JQ?qQC`mK`GBP{I-RaypxAwNyobBD(?UwP~`J4lZdg`qBR=apmz{M!iuSjWovc^s} zn{QIZo#DLS>?KdM3%obm*WMV~X~FX)S8%vgaDF4L$U;4-4AH@g2%k$>daf$p^h)*?_O+)dtm0s-Wpfvnu?`NJ_S)`Z!%!%C``NZ zj=*mQr}JKNfxZI0?^f|t^<^g&LYh|C>4!Kb$&USz#?tEen5%2CZX8L@*)Y#JHQ?(B zcXl9zb)0tCS^_Oy|$6xIXj03Ju!R^^!$}L}{&7<{+i$gU^bxdDR3W&Pnj^-IQX-^b&ocr81V zLZ%6SQ0MCTzzKxsBg}lc?KX zPJmA@!O-XcPy`?S4C+yF7)k2OD)xSL-zj{n;_B~#j?mjqe<#oRd_f*;Eup|XjaAzf zcNgKe$7$jc>sO8w0p}4fENA@xOmMu$*xemLIuBBBdk>70N5p+uqR{an9u2lyoD5?C ztC;k3$Zq?t8L)Ea(?o;+LW--Ny8o8MgILq^ttZd?aCF)4mz(rXM@FdaNf5CG?q?Y+ zph%L2CrJ_20u5O2-WseOK2I zIM7OGczSJmm9is@IMt?j7NDk^^NvWfG4;46L&!B%TG+O2W5BNTW?RJ zyjyd8|D+_0y=Z36L9&pNF{~pD?TBc+{Um2XtNQ#0iKt#bdD_xsGBnCcA?h4o>FHrM zL6dx>F|>3OKN90`yL@XL_{Q)}@&W#2y8kRlWO!KHK1wanyKQOR7#AJuOfx9-R$!Pe zfb8W&Z=pL75Du2N#Bv)v9pU1`edkJedI=x~kg2d05iqzhCw1*9w26Hr*Rz~5M7U89 zn&GDA_!&;ki)35G|mxmU*texy88{=rtV$S8>^$_^VZ076k5po*4X|zC+k)i2WZNijD|OyMak~^ zC@O3D0R>Q1cdLfAE*k>Rl@Jp5HLnq?mWX(`>l%EGx?NivIzR`kN@2F74E|0Tx!>XR zj;Hw9v5T4i`#z`N;`m=hPV#hL7^JG}ooixoxerH*&lwh=k22maoyt24A7nA?P_MpI8QN~UA>CP@{4{&KojmcwF@e<3;(1;; zFHde|J#cwR+i z*`%lT@@L=j=;mD6$Ia&v>*CCA(ryts3qL)zQJaFtsH?*(X~QH)hp1uBe>@^a0Oamjr z0s|Mr=!y zCtHp!=LhEej200Gj&2jphW$-MbPQsDTg-9QQA>JNHp4<6^NEBbKy)QvtazqFxw#}( zKN$<>s5RsB)SXt;*_s-DdY}xT1VTRsWomr6v?jlW;d_PtOI;Lv7X8dr&}#>SQ58E{ z+k8Xc_!&OE5av&VeKPm>-}3^>aQq?PC)`PmtktycV)`#fb8Kk~Oy?;c8 z_jhaiyt-PgvWJZNEf-2qM0Q$7+^kB(cwoW?{Z}x-j|`fxJRqf#Fv(U%D6#pU%|C;3 z?kkmJhO7PBV5R!8-6eq|CR&^Qi{;&gnApbLy^SRex9fB-0eE&NM_p$+3caba;e3ny zD{_t;!0zK0$)SsVhsHLd9mL7C5|(ndXPODooW#E(tD$?G8v_-&3^KNq`qdt3vphlz zZdW}V3a-SajiJGcg=1OFs(wK@?xv$y&REhA5G~5}j;!NNy0`R~!p3t~y1Hp?$FY42 zG7XkjO(;jisN=Ck(Orm180Qlp>e%nKff3iLx(%DbOa~sI(F#YuE+YwNiKm;$pHKV6z-+5T^rL#BANK>~Dza%?Q zBqjCg_6rw^j~v*iZzE};7e6$&nzf-H7tV8|Og>nS#IW5*$4Ua{Ww9}4n{K;zK3b@h z@_^(keMl=xCPXAd2T~c2#I+X7$k1rF`jWzwJOL6bTzt2JgIgV)_^Uo9k>#h|E2W50 zF+fQ)R^4h(uzBECm|8>6-<$dG0jocuXaC5IL=!@Q=&g?z5Wua!rL#yfB7PF}lzqd`ktrZSH~826oYwI#FE1+bkhCKeT$oZ$lZ zm3ltm%7dT6-{n1wa8CYq3M($N4_;5TkFeroPKFQpL3pH9PG7T<0jsL05`N#uXx z(>f5ZzbkrOk$>&{Fhf$4MyjnX^X3Doj7ms`t>4w+`mWrIoXLv%@@w+q$s6mx2U0(c z(jOen(%#N5u{`(axeNO1i2t^oIroK{oYNO=IH6C=a>ZK8kGq3+LayE44om&&c^kl2 zeX6#6$P}FaFdl|*&%-b0l3%@?{dBZrd;1BzX1i~f7zhfRO$!adg4EPDIP}ZPlCuA8 z&)vpnm5nhDc|@-0z({FB)a*$%_aK#%R+zatN``i9B;9b&PU@86*_V}u)W5sdXVJG3 zm@Fx}4fOT;?1=AGho9$F$3%Ta?TX8LAN zTeuAmjquSfDGx~LK1+O?G)-F8rVpZk^oK-It0Y?(r01Uzc#Jt@8$A)X6R>h zG6QmL%|079_o8*eYnv7iQ0N4nf9M6{CFj z2OUUt(Y^>x;p%8sFf+n`Ge{2;q zR8x@TBa=LrGNabs(x*N^FHBLOvStMR|)owuCR$DD!8E4yEU6h)( zcpeK%e-R6UkH(X9%7?JFo#whCCyglG*ne6_It+#OFK$Sl6?3%dtKVL12j^fu3paTB z6?}*K%<&LF0;0-B+i_8+M;sfUM`CL3wZ~mQ+`TtSlJb?~wl;A2okFuew zO%0JvwhETFM_Jq8SZOQjFMWs!$Ct$Nvd0}e4pm06PqBUKeMW?D5LW;~yiU|7Wz1=g zna7i~#nVs$19|Zmcj|iO8W|9Y3~uGW&I4{~=ZaHq{ZqXUckphG%A`hIW3%>JP}bx} z{HB;iH1m-4?3trmreCB8y2!$6~Z33Hc2ogfIaf!AX06`=b_KqGt4(Ssyrg?y7dh zF;HTU@<&P7NOQ76nydqQi{#~s+?5|BX17@8v_vV6ul+IH9%b8{A3Th+dGGT4)i-Ks z&hQMDv$6>D^ZHreKi2#%m$2?mUJNNx(UUf zTV$CtHDh9Cs6#tki97xGKTN~W!VztX|204T?W|ziRYy*TU%*A#v_wwH%!GH|SlSQ$ z=i`BTe#>9s)Qo?6RIrcQ)o2`>{reGKwa*f^y>M_j_v%i6^V=NW$}7tblr00L$T?#7RFSot}h;Cb%nSCvW8TVB*uPXHppxf`QK zaA*j7#p!vL_MqBD@-aSAy|L;loRN`-;`gzu8Slm$Os_btZA83{pL%3FOx5~F?wICU zod3w>jV16V~*k^9Kf!B>pRyeOf?;_=1i#d&m0X{=M#phe%36WIZ zWo>SV+OAfP`kQNlqfc#UP0u#IY-7t#y@1{JuWg{x=XftnqRj8{D`~Quab6z@3 z-0Exd`Z4s$DmTE-j2!vm|Bivo2SWd@gvj=~=OKZ}WccX({ z=Vp_0JZfThOENvyogeueT*MStL~41^vEklb{*BsofGRL0pdy4?VQ{!z?3+M%d5Dr- zUhha|Ms&7++K7>#Dyf_qAqf2*(vYaG^K>E77_d`#k+}Tfh2`|wO~mbz9eQLSCeH%rOlX& zOmc_mJ+4QfN-+83_~R`)R+E*#Pg}W5CsCT}QaA7?vo3)TT|XC-WO2)0zkf7g?s9D{ zj2?9&U2k4E+0U9|pO++DZdIvL6Oev0!vBs*aTj!!1wl}N9<~bw$>VohEi*?vn}tAG z<_MM2`C0;#%Ias80dYk6IgkQ~clFfDW`@;{C6x=N-P(xud1GcPanR2|!;h~SXsQjR zWL-U^%-TPlUW)rL#1kx`UMHoSWNf1l892r;e1s~*Zpx*$K+qV^$Z7oL%63r#77au7&yNZQ)Cek(!mgU`68A&T+cYj7uZBWFmLf%q{} z%#xfo@~?rFZ39b*0`wrL{XRoPO^SVM#%S26D4n?t3L~a znf*|k8z~xdpVr%6o$1~*inojx?~@IOk-J9} z>@mHGwYW*aPjQoAn!-X`REqSjwabiDrYA~c>B^xCDy}gTh|>$N_|-fmE%c|aYow$- zFBy-8*)LN*4AQe0(F9W`Zx)*U0LQ2!y~z_ce3QH$t}Nqd<@;Gug?$^-EBF{C+R1BX zqT25T0z-H@v^M#jsw>>am2uU*aJoLNe6onekeQRO>{DeWg}x)}@T#9fGv26H8%S%l z{?c4^{bMB6S8004A8Yb?)L(JoN+`slH+7d>uXbNO0RaS;hX;mrH^HgllijHg>5_Lz z8*smGds%y=G#Jzql)IM|6W`gobF=&GMD5FOq>=bWj=JnBxp_d&i$ara_G=(!It}D2 zMY$(b6N&pp7U0|=&uot6uJFX4;Ts2(%(1|ksTrM0gVCn@xW>t&ve^7StrGB=h==@= zW1)rfAf!_lR~U%o^m_v57(}$XD&X6rwrO+=SSB^j zQ!GwjCvm&oy|HVBc0TYG>58u~X}3NwYKzS( zs=_=8!d*PmFAZp|dzPT3@|l77$jYH%#Nse+*2IP**!e?EthVw;>n-dbb<8cZthHe| z{xTpwN_hiWW`#N}Kfm{C?Px4kTEO-rSOJW=^AEP7$>y8&MD^Ia!05FV2*kk1z>s5I z`~u7IB+KAJ9gqAYBq^NNqq`5T2x1IQbeUR~FKk|tBn)Bto+^X18MQ6I53DHHNi4xD zQ<)H0B@<6H4~B4($#L&@>FoU~o~(hmgv4n^nT&I4Lafbgw0Nu7@+;Z`1kOLIIcQ9l zr3Ngd|HhUhFe3(qwCno`hb9xfFxkP$IMfssMy@zs}=l~C{pNHCiVI}NOHSMmvW66dR!U4a3t zt+OCOl_t4+k6`Z^!7LbTGbJrjZ$REPIg|CGBiO-_qd614rOLG-%Io2?t4fkk=0v53 z|ARLf8-}mU*>YYfq-3$VG*Lu#rP)yT!zZ1-5tC)f`9>6%c{N*t%y9Cu*>;x^0WXl!S?Be+$ztwxo7+8(ozgkCMf|LZR3Gw6 z8fFo=;uP!LT}Gi5Y3DbSJcNyWUMKw17fVUk$Cz!yjH3<+en1LjhBSyISB`c{Xc@jI zE=YdN?SSf${*14@Awx$ZOT>&wpz-lG7-T5bL2af}DDDK$G@)C+>s*$*ANzCw;;ark z7;;bc5;VrmBz*RKO%@P3?sl+}Yy~=1T;cY2MK8%d5Cf3;210+bf;sTXLeEJ74A48+ z-pKepeuCdkRxIwZtnYHSQi#INDd0^Q4eH@_B8)!ZU)*wB;S~5lC4X7wOuk9y(H)f_ z$iV4FD1Ne`srl5)utL~dw}I(w^-s!@#p=!HYkF#!L6fu0UAFcAeYrU-USInb>oP}dZBm28gUw2Az+ zPCP1OCHO>B7id7j_IOVDg(O3~vMiDufO|I=DjbV_|H#caWvC%4iwvWb*}vaAoX_at zLqrz@Lt>~?!-*L#;M7m5LjX00wt|bi%9IN2KEFiNBk4Ck^QQE35u>ah%nb5&K5nPR z4urj5$*Vn#BB}-@?~lk~zk0#drRCXMh?z)rV)Keo++CTW>eUHM{hX%l=^rVDLv$Ou zD$X+rP{kqz+m1)a%gsA~(ASBS)98LkN2*}iw4*{)$Jc~`NgsA}1(_%2W1wo=P67C4 zlxH)!>6CT?k#)a&45`lSJvX}0Fy^5^D#n6*Nml;Spd{zq52P?KpK?aS=9#!WjmCtiiha6aIbWn zQ(QctlZO>@f&UC=N}Gci4-RAFWM;!&F0#@PCdTHjDW7{X+Muc#H|;`y_-(xelMuc~?X^DP;A9~DjKT`gcRlQk_yrjW$dP7P6_vfqhG zo?0{^3Jy*%oi*7X{rI5lp!3qr9tVlr$jGQBA&o_!=Sa1dygj4ROjP{z*$LjkZ*OzK zrpt>^_w|)7v)iO%IxT9CBfB1hz&fHm zX4b0u;^r`Ey~u^A0{dpvH>P~+Q{7Wh$Me(utEPc}BA2I3C<=YJ8Y_)BotDlVnEv18 zA8&d;v9U$gr*oa&EUXlqgO(qDnwuLvah4#RX=z;QGiu{NS%G|oWRD45bDP46hOx7_4S6Fo+k-*%fF5lweBoc6VW5Sk`8O^aGvJ5I!k=(sor zBs=6JizdCZnJ8TUStn`7{=Ii^&fLy@=$vV}@#k;9)1IB(xcf1uSE{W43!`^$b!%Q; zcyw8oF+P8h8S{n1uO`SzTVAzc_9&WPx3c5JoDPq*W|@C_dd#?VE=4yUxa8mX^vtC6 z+~npsp;-^ijc?wW(&E5i$JrPoFt1JK$AgBIMot&Sc9ruJW?JZ;IZteNLk_Ps^j_4gID2L;rDZ z4icLf8JT~jdh(i&`&BoK&Nx0j>ciop@US-5-P~IaD+eoW($~3X%3h_psmZAF-nK2e zY*vqb55*n3{fM`_`v6nj0lD`|0f+Y&O0;Zk5V{xUz?-kWp{Gggo?il|x8}`Ib zESs$rZ~CE($zUyW`w}_7;KmRIQ4iLj#u+SmEq6Yw`=y_D;A`kx?qfBt`8Tl7c#wF9 z$=K%GO4}oO4@{e<9M%&!E+q4eOK$y%_Q#I}EKa4`#2I-UU9x-IfkHi|^EVHPZ)S*X z6g qt)x7$D3zhSyj(9cFS|H7u^?41zbJk7I~ysWA_h-aKbLh*2~7YlR(XT~ literal 0 HcmV?d00001 diff --git a/DSView/icons/fft.png b/DSView/icons/fft.png new file mode 100755 index 0000000000000000000000000000000000000000..f2e0db4a4252f3b48e64933028606533c98e8fde GIT binary patch literal 1411 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 Nu()Jv^6&yY>3<-rR0IG3 literal 0 HcmV?d00001 diff --git a/DSView/icons/math_dis.png b/DSView/icons/math_dis.png new file mode 100755 index 0000000000000000000000000000000000000000..4021208564632738ae309a6c0bae7445f7b7b8a8 GIT binary patch literal 2961 zcmXw*c{~&TAIEq3YKD=c%#|ZYxoQ=WbFL8~w_;*rIW}63jLg}ATsdczbh*DK327=6 zlbVui%u#NQT%~XO&G+~F{qg?1ACKqj^?5zs|9l?r&vhq98xbLd5C8xWLEBoo@VnD) z?fHv;W{I!W@Eeq9>vahLfJy8Y5Kuzh&xe8}w1br(M*srf2b}5gHux7=d6LX8lY$A6 zfG4#~TRsvd@sTC=Qa~i0NWv4s0TNIp6+Rc*1ghT|^=X0(4Ma5ai3HE#(l1>;8 zZ|&Pr(?v#q8$(97e;e#u#36Pghi{L^J*F97 zI5TGnqI3g2fQkU`M4!YE=F;;H@}`16g`ve!;skO47-6DJy%)!9?T^x46eD+(B==8^Gqq$ z?tU+yL$XO?D+VZ03J{()J!4&n_1wFMdfj89hB57xbg%E6L~gqM$jcmocOqXaxB2l! z`Kj@g=L|K@m*asn2n{?gNQKLZXaRkDKkNh{TfJD|n7Aliq7w14HLZ~A_z)JO&ei1H zVmxpQoCV&Wg2g6aj9+jy#^$w?R9@?v>l5{frNzdg2k@%(GR-pT#;`Bx&0BA>eMPAG zXKB1Ch)G5|Id4_rcQ3+))9T_BlZhGM+OvPV9oMG#@)NIl$ic7tx+Op9o_cN z7;IaD2=$Hc21cV1K4uO+2GaBK64X7XL{8n3?W+LJ^u5Uz*TBmWLu|)I$V1HVH&rvH ztGLOKlZLEiKW8ci7tct`{<95O!(GtO=7Kn8Z60kyOkV*#`Z7bx9Os^u1)m&57qMl#I{1;J$lHqR^ZrV%uwG)5DdTS4Ou`2jyw^RAij-`t<7e4-uOkP_j%6T%@uK zWpTFx#jPBHjb*nhBGzpzBCXRZQy7BCz>BY#z94tC&Pp2_1PV(z`zj)@_#f?fr0XL$ zrC94<3E8nQBH6Z9+qpn7bviXyt>6P#Se|nBbp$GN$Hqm8tCB1FSQ#l3J;n3Kov}A; ze&0~tIoj_WGjmpGW1FI4K`u^x^Kn^Z@VIux7yA*xJ5|??^f7Dm)*(bmJ$X;*D=Y0z zodchv(<&Ya^pvLHrjxGzI;vo30z92VdR~0q!|aUPrvS>%q&a!84*AwZfGl>)yrXQm zx9Y8*tXhJ?p2$aO(}ePncXzm$$5(C&&nWGESG zGZi-Sj1trrT3+!Z!X@A|uvXv?hy>t)$0_iVaKS%WM&mCStz7YMUap2TEvjUXINVk1 z2t7!ZElM^yx7Zc8kg&Rn5msxvh${?)rW{I;{=?Ew?_ESuRzNFu?{vG$M*Bg$4(+Ih zVz%sxpM<5NK7ICJndqrpkF4xys3Ye$$rF$B%l-t^0*LkuxQT4IPCDmqY9DweUAOX% zF`ZUdT<_{$&Tn-r8isQ{f1d78 zkbPQp(4m7GNL})iiU+vO(9E5}$uQ2#Co1bl>8-2IrEJ88C;!PPa+~HJUIh6!ZcSxV z3Anm19&K#J4Y8q{Mvsgcnz=al9Iq6gi2z1Ji@m-*r?E_rR*FO?b#3=<(ZFk%UH`l2V<)S>@2JpP*fc1h-#?gV2y3*m& znEj1rte8{GMNt(US#C#4wPrVDqBCID{nTck#ZhcaidfAIM%>X?oZTAkjXZwZRQk>= z+cU89NrftC$Rts7;ET|LV`Ug$-fH|tyQW4t_))c9wFFXNna9Wt-s`z{ciYgMMAzh5 z;?1ZLu%wKwe6o#!E@xCa>h?;+JSitlAr_^ z?xJc?)C2m!3QiI5HRIjhD06jx_2`H!&%iN5Kt$uU4oYQ&v3_NKFyr(0*$Q1rPeK_K zt(}XrZ82xv_$`-drLu=AmKD+3FyISE+khpi-WDi}5lq}UJ1bf5bHWS|=A1d=QaP*1 zb#yVu(@($tNQ-Zt8CnAIccBXEYKWKo1Jv{2He$ebpXfZ8S$#NnG~-OTZg)+S_E^b9 z+_L#Y0fT)LW@T!%;O0+E!^*kpv?ydL!gfUNys1I{s`8aik{XpMEss8fao}Rt?x9+0TBjC3yKUnh?yu<#EeQB(oi<@5lgi&HU2gaW` zv=2MVC}ISrx*17@P#v*VAvS@>-A2vOvo1;m*>OjQ&Y;04`(q74;Bj$V@9Q2NXxFzb zrhBVC!~7)6`jK>b>C%h(0moRHih4cXnBQws7>K}Aj4H97 z&=Cc8tNLx-gvlY$KI_@An2b1BcxS|Rq4m<0Mow=kyf>S$zo&_E-u2qTX?82Z)}*&s zVf+Sk+&>f7bgJ>P$7F5qiLv3bbY1re{4sTmN&DCgHM>s2Hp=Pp0I&Zv{9)Jk>q_+HoRdVfFH_rL4)x~_Y;J4uPFhyws1g+$nU3U-g3 z5`zfVyj{zW1sgOL;YR=f3D{17fRZvL0gA>W-5f-@BEm9C20l~sQoqUJO#HEg_{bPM zP+P}B2uLzsK=vp?7#iI~_6cuYiOJn%?{Wi3$Y--<_t?nE`? zF<2B}$&-N!I?$btR}4BnDHM+a0%Q3*af#o0=WG8kS6Mr5B)Xa1A1+z{e?T?)% zmVGY^z82`D-f4&NYS7H^Hp6+C?m{vQyNbmJ5eCR_}!i6BUsQpZx#gj;1*r4P&q`s3Dnx@typ# zb_A%b_K&-;A79C|qfJ zO@nN}>R!KXlDz1^={^Vzj$zHgxjc(?FFSy~Z~9_Qd4qQ5Pm65P`umTF^kJ&ZnUmYG z^TAA)reA@vnEZGSD-mG45i#lfK}@PC=UTtxpA)*wZ7q<#GSs!SzaGjHH_qAi2T6jH zirHDcTVux;Nj%GI2XnhtF3+wo3w1wF{fI1GvJPEAmYH10ruR7KlhBF!^>wn_XYBd& z815*1LXiv!5=t9(4_{~xyUa}(n<^Hz)IzTlG{;XSdwB-O9w;4d*|j0Ll)z=^{?z6` z&^|&RCNqPIR!4o7&WeAVTIh&NaO-FsdbFjuA5F{cfPB@eZ+O3buFGg!SLJncrs}{i zMx5l7e}{P40zrOk_aS-d@yf_UkAy&?@-!J|#x zmIZZ64A?+9sUV_dM)?h2WdA;eRHIGXOS4*4m6n6j+h!k%8=tnaTv=+Y98#rZPmda6 zQwBG*?=SE4-A%*8&d2&1b-;A5CLHZhzhXg0oLBgrp=)VSt4kxCZaMkiqhvuj?X+R` zoc1ffs0@lZ6F&0Dd0&5%Znt^u@~707t+FHdE)^}Vfl9Nw3;B^^{#~!5nt`yLhha1bq0(mjuJLnKI^5EsGB0axRs6fCqQ22U$>4y{ zL~*Dnfj?6nS$4-YlZV3YJrtPB_V2{T9qq$X9Pm-nS)xX-lr{7LFkr;Z*HA4u<>_bLWiwMoZqi zePiAs(aK9z>`}ioZFj2j?J1JJT&_!D7}_N^XY)16mE@%J-6WbiG&UKTlH&jT)p9bn zo>fEJ@F`{8ugUwv-c$YAb&B;D}hJ_s#ExR55E6ob`_d`{yxC>g|Lj+T?ky*x*4r5nc z-Lpr{XWP`D4{^S%7m*(qSwL4kgE3o%rW)YHGI`zDYcGiqwT{~jwjrDxQ4b;MjTAeV z{P|4H%Ou`rFSVeAy$4k1dOpkVj?8IxLe5ZDagN z{b(B9d;5ri@CY)Mu&&2_f=+BX?-<)NKw>lZ1k?h)o!|lIr7FgeoZ+Yj6{~>()q-pM z460RVHKq6SUxSWu$BV6_<&3LLmKy;RucTzy;!Cp4SK(TKkcmdy=Xx zLC*JbtB}z(3WPO5_UV{VIkk63LTA7bhBQd2u5NC*QlEP^!q!5){dPSRsd~;_{-=29 zU_Ke!J(GJpNn|95ar#Sdu_SrWFxXCd*8y(yFY3Tl>)xO`x|EjlQT|&yF*WcQFQHpG zfCmKFtWW%Dv5Zi8or`9^M=fv_^XK&!pRY)D)-W#0-&yEK!{=8R4_zzHxrA8FCm0{h zh*q|uRDfusCR>t?bb~Q)N8LoZWEV|$1-5OG0Z1}_XSV5GrhWm{t@CI!e z(Uz{eam^%el#`}%4$iwr=9LdfU&n1TZ%KA7DvU}&$2d5hxdE2;+8v$Qv4Xv-i>>J0 s-rr&H(`;rfP;X49E~tzb*uWQhnr?SPPxAYk;JX1J9o+5fj)!Fa2d+AS-T(jq literal 0 HcmV?d00001 diff --git a/DSView/icons/single_dis.png b/DSView/icons/single_dis.png new file mode 100755 index 0000000000000000000000000000000000000000..4e1221f83739bd06294778e61b0f596b17d90473 GIT binary patch literal 2443 zcmX|@c{tQ-8^?cRiLvBJ&e)PYB~*h%WoL#4Av+O{M#yW+k~4a*_xy8H{e28~uORm=N8W;ofhynmnse=Lmg+($vD1?I}tb{m%0us{tn=rb>Ph{neGsELT z&{&|dmJZ_~F&qzB21Ny6Q5YNw9SKN*l~s9MzQ_a0m{lmY^H)=;5Cf8{~gr zRl=bnf&l$Z2{2CqA1KafBre7u8wB`Z_7Ci$Kf_T`0pb2UD;A*88gh78_yD{4V{`($}4G03(_iu6kXR|;@Lxh$+(eRuDMI~Ns&espKH1I!HA&y!*53=>8^?XJGT~2Rn;z9o%ip1 zYw|WS0OeM&ZT{Szg`D&fG9R-=+5DbC zqEbpq0&mG;y!?I-GhG-7y)c;ur(`4aJWZiYJ_WwZ=lWEa`MdYnY(gtPMBOLL zHWqm~e`;aG0Ynyz-(%YZ-^I%Qt%EwFF|G;fUj%bda+GHlG!SQf=tlaK)wu9QG1gz4 z2PjSku_Qu2D_>Br9+J8v$1)OKvy%(ecm7JFF993;6uqWBJGimeYo?RU7+vw%>)1gM zMMv)&kXRDg`Q`P8q=^c~?>ec-;Y{xJFP&sQs@6P4i&TK7hs&>Mm8wD##pfR4n(x;* z@fD8BaU7a{)tweu(T7*`swK9Zt!EP0&TNN0V5_cN^htDYw(wy`T}9eJ-PHHU!DK)F zZ|!eVK)RFLF>QaCF-}66;9Vm~v%>83_><@~uIYZ}x^&tm+~{biYIi*i$MF{0F~r!oks)dd{#?~rGwwY$PH`cK@X{&RBMNXv{jQZa4Yd?i3~ z?_EV|jMsgyK)G1)tq83LEfP7>X(I{b$47?l6pZQ;5>4h2@JnrP!o&Y#&57z5N!`z1 z%Yn%kj0(TffvlC4FY9NHxR{!xoX@TbJ4YSAF;4M2TmNdJ!NzIiHFHvzHoOFpC$aqLYdYtd#qcj62WR$I_g?y>&(Y+Fjc-S; zD(3hMMwsfZtJd2YvS-UH3CRXiAvBcf%%mFHs<6oD!flUvQ5uB#$|^u`MFSd)>LTEY#Oxcp zOX;mZ7`81~O~1G|4XgUVMr0(65Nz8a@m{n6Y)BTpED>(2T{0`X(0PkQ9k3n6Pd=r4 zlfwEI8_3|n#-5hX*~Jy&$S0s|xQOp?^{>OKkYPK7Mr=D1!&Ew#53)#p*HK2B#ovkl zRtsYG-VxGX8d#OTDcAq}qR_<$A>M@wP_HzvAzhsSzKbgJrXG(!Zy4D0F_|yK^GCvN z1zG>uNf`@Nw8GPsi3_C&wa-^y!p2VdslI6cO&aeqo32_ZaKHS>8KNx>b_%3Q+T16d zAs38#hBEc7)ewK!tC9%FVR06J;vouR@gl~a@w}CN_wm9)2g1ALO5TRT8vo|@a@9>y zXZYP;KhljbHZ_d~O_zd@TIvYC@gdhS>S1~YA>xD;SfNy7LK7q4)*DZN^OjZ^^O3z1 zmxhA~oD^stNhx_a6w(L*uJ13}=1!!kq!UI-OdTk? zb<<=Cf4tws?G;|)mApHbyi?iwr+h)~5H7Wk9-ruW>GHUwAKEoIgIFdHoicexFk`Rk zmr2DmcyvTpT-ZXd757T2MzLlHnWE0;Gqc(U0*|7L~o;^!v>Z^4( zkA+_A@`bAsy9gOrRE|D8fI>N|JYTx$FB03yUf%GjRU+QER71eVQnkc)KIADvnF=9D zOQ#+@@Ud1ZX<(xFNp3c-mf$d>oBhCrt3H*NK>uc$uIxj z(le^d1z-5g`R-W9&`%8`n6IAG`YP9Z?1_fo3ilV9W^L`_3i-rj`#@rd(bJ|Q2Fj>_ z@ZZX=`$Of=)Yo{lT%D^WFu9NJ?T!*bP_HlCZqR$d#Qv1%o#v6wzRwghOYmIEccIjF z+74CcLMB&{P}QA$CuVOxCD#%gz-LFLp5MR%^e%bvcTpHuVC|{P5aI7+DgoUoTHl%lIa+LI6Ydkk$ xM_V%TztabA6furJb!7)lA*L2O_AhaZhz Date: Sat, 14 May 2016 10:24:44 +0800 Subject: [PATCH 14/32] Add FFT function @ DSO mode --- DSView/DSView.qrc | 11 + DSView/extdef.h | 6 + DSView/icons/about.png | Bin 954 -> 1059 bytes DSView/icons/export.png | Bin 942 -> 919 bytes DSView/icons/file.png | Bin 1012 -> 1886 bytes DSView/icons/file_dis.png | Bin 641 -> 1896 bytes DSView/icons/instant.png | Bin 1081 -> 2371 bytes DSView/icons/instant_dis.png | Bin 1263 -> 2376 bytes DSView/icons/math.png | Bin 2947 -> 5671 bytes DSView/icons/math_dis.png | Bin 2961 -> 3589 bytes DSView/icons/measure.png | Bin 1136 -> 4115 bytes DSView/icons/measure_dis.png | Bin 1002 -> 4137 bytes DSView/icons/params.png | Bin 1210 -> 3514 bytes DSView/icons/params_dis.png | Bin 1162 -> 3502 bytes DSView/icons/protocol.png | Bin 967 -> 4180 bytes DSView/icons/protocol_cn.png | Bin 1243 -> 3269 bytes DSView/icons/protocol_dis.png | Bin 931 -> 4195 bytes DSView/icons/search-bar.png | Bin 1520 -> 3679 bytes DSView/icons/search-bar_dis.png | Bin 1388 -> 3671 bytes DSView/icons/start.png | Bin 1079 -> 2183 bytes DSView/icons/start_dis.png | Bin 1351 -> 2179 bytes DSView/icons/stop.png | Bin 628 -> 2110 bytes DSView/icons/trigger.png | Bin 840 -> 4093 bytes DSView/icons/trigger_dis.png | Bin 820 -> 4101 bytes DSView/icons/wiki.png | Bin 2787 -> 3561 bytes DSView/pv/data/mathstack.cpp | 238 +++++++++++- DSView/pv/data/mathstack.h | 118 +++++- DSView/pv/dialogs/fftoptions.cpp | 254 ++++++++++++- DSView/pv/dialogs/fftoptions.h | 86 ++++- DSView/pv/dock/measuredock.cpp | 10 +- DSView/pv/mainwindow.cpp | 20 +- DSView/pv/sigsession.cpp | 61 ++- DSView/pv/sigsession.h | 7 + DSView/pv/toolbars/filebar.cpp | 2 +- DSView/pv/toolbars/samplingbar.cpp | 28 +- DSView/pv/toolbars/samplingbar.h | 3 + DSView/pv/toolbars/trigbar.cpp | 76 +++- DSView/pv/toolbars/trigbar.h | 20 +- DSView/pv/view/analogsignal.cpp | 2 +- DSView/pv/view/decodetrace.cpp | 2 +- DSView/pv/view/dsosignal.cpp | 68 ++-- DSView/pv/view/dsosignal.h | 4 +- DSView/pv/view/header.cpp | 15 +- DSView/pv/view/logicsignal.cpp | 2 +- DSView/pv/view/mathtrace.cpp | 485 +++++++++++++++++++++++- DSView/pv/view/mathtrace.h | 155 +++++++- DSView/pv/view/ruler.cpp | 2 +- DSView/pv/view/signal.cpp | 4 +- DSView/pv/view/signal.h | 2 +- DSView/pv/view/trace.cpp | 25 +- DSView/pv/view/trace.h | 7 + DSView/pv/view/view.cpp | 343 ++++++++++++----- DSView/pv/view/view.h | 38 +- DSView/pv/view/viewport.cpp | 572 ++++++++++++++++------------- DSView/pv/view/viewport.h | 10 +- libsigrok4DSL/hardware/demo/demo.c | 11 +- libsigrok4DSL/libsigrok.h | 3 +- 57 files changed, 2211 insertions(+), 479 deletions(-) mode change 100644 => 100755 DSView/icons/about.png mode change 100644 => 100755 DSView/icons/file.png mode change 100644 => 100755 DSView/icons/file_dis.png mode change 100644 => 100755 DSView/icons/instant.png mode change 100644 => 100755 DSView/icons/measure.png mode change 100644 => 100755 DSView/icons/measure_dis.png mode change 100644 => 100755 DSView/icons/params.png mode change 100644 => 100755 DSView/icons/params_dis.png mode change 100644 => 100755 DSView/icons/protocol.png mode change 100644 => 100755 DSView/icons/protocol_cn.png mode change 100644 => 100755 DSView/icons/protocol_dis.png mode change 100644 => 100755 DSView/icons/search-bar.png mode change 100644 => 100755 DSView/icons/search-bar_dis.png mode change 100644 => 100755 DSView/icons/start.png mode change 100644 => 100755 DSView/icons/stop.png mode change 100644 => 100755 DSView/icons/trigger.png mode change 100644 => 100755 DSView/icons/trigger_dis.png mode change 100644 => 100755 DSView/icons/wiki.png diff --git a/DSView/DSView.qrc b/DSView/DSView.qrc index 76bf2800..5968cc45 100644 --- a/DSView/DSView.qrc +++ b/DSView/DSView.qrc @@ -57,5 +57,16 @@ icons/start_dis_cn.png icons/settings.png darkstyle/style.qss + icons/export.png + icons/single.png + icons/single_dis.png + icons/math.png + icons/math_dis.png + icons/fft.png + icons/Blackman.png + icons/Flat_top.png + icons/Hamming.png + icons/Hann.png + icons/Rectangle.png diff --git a/DSView/extdef.h b/DSView/extdef.h index 549c0629..192bb8f3 100644 --- a/DSView/extdef.h +++ b/DSView/extdef.h @@ -29,4 +29,10 @@ #define begin_element(x) (&x[0]) #define end_element(x) (&x[countof(x)]) +enum View_type { + TIME_VIEW, + FFT_VIEW, + ALL_VIEW +}; + #endif // DSVIEW_EXTDEF_H diff --git a/DSView/icons/about.png b/DSView/icons/about.png old mode 100644 new mode 100755 index ac3890958c56d5ab27b7f7e95dd117c3439ce667..c8f40c627d26318641b74f55b2678426b57b357b GIT binary patch literal 1059 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I0wfs{c7_5;mUKs7M+SzC{oH>NS%G|oWRD45bDP46hOx7_4S6Fo+k-*%fF5lweBoc6VW5SkM7 z1{amG<{Xu1^js*JHNp7vRs-kRmf!E%`)_`+dw2DH%lq%|ReqoM`#p4z>5M{D!O{O8iEzIgSO{^xu( z;fU~?P&?x#62)_>#gD&S{(hD5m9zi4)?0dhTUE5NADq>_ji^AAN9S4%grEvy$X ze)i3bA-eTnqBKyTYT^IGS&SaqA1?A8F#faNqJXth`yYRT>gUM<3`re}Mb|PIop``k z#K&@M>w)B^;?r6OWQ2Dey}nnX(kE2cSu>jDazjf5r|@b z!>ye6nQs>37OS3r(hS-P#tl7(UqrF~czpYVzIDg71A#IXhMRUzVvXplXUO5`)c5h_ z>;~os)e_f;l9a@fRIB8oR3OD*WMF8fYhbEtU=m_zW@TVzWn!XjU}R-rz;Qa`4~mA| z{FKbJO57SMr(JLWYLEok5S*V@Ql40p%1~Zju9umYU7Va)kgAtols@~NjTBH3gQu&X J%Q~loCIH8avwHvl literal 954 zcmV;r14aCaP)004R= z004l4008;_004mL004C`008P>0026e000+nl3&F}00006VoOIv0RI600RN!9r;`8x z010qNS#tmY3ljhU3ljkVnw%H_00S;bL_t(&-tAgjZsI@?tcje2M2a9%evm-1NWwQq zU{7F<;Jra^5cUSS!Llcaxq)*7zVl%5ccO@gNSudV`+<#Yf>~h35LR1KBV<8Om3w+T z(``W#n~be>6Oe@l073{UC%*4z0n`9g0;|sWxecHdn6&^+=Xr+bdA~i+qeA=bdEP=oNJ5aDdkTGgd>jL4VD=P|g$=;eG|f>e;QjsG z2*~GBBp;#+z}(*6ZYmM*_V#uGpuPg)lc?|P?EH`t@cR1t6M(q}@*x_#ySv|$1ZcDV z>ALO($#s$k0Mm8dP9~F?8i~XRxUOpeu+~gIidrUqCGPx8DBAg60vP8)ECPeUz#yqh-X$chTqI2ezV*RiFph#Y zIe?;@ZT96?Pw!NEaq908KGbu(HJH2}Y&0xIifv>+;J z3aASq?8%EHt5CNotN@WD4xFokeRz0yJ$doz=}F-bqccfVfJ*06k;WMRMpfeR@$q<5 zBcK7m(b19f@bIv?0@8B+U%-L_Y@}N*Q7ym*;;T?AP4X$A1;itex&XxyhzkH;S z2RKhF0*Vmh_enspSZtECzi0&PVzJnqDIgfgB-xdz-#Ctwjrw#PCo5TR>P{;PB-Tbd zoz9o5jq1AosWzH;9?*5YNwThR>65YgB>BHaAVj;}UVDF_R4OI*2j=R~wA<|qlJ!+H z7edrarIK8SCdB>y{mOe}CnqOrdSoH)?(SCBr93@7RokVE;`a8|z^F>BDea=9$^u7p^{ cSvXDn0lW191`&7FfB*mh07*qoM6N<$f)ypOlK=n! diff --git a/DSView/icons/export.png b/DSView/icons/export.png index cd319389389a0689dc9bff3a6e4bdeec6ba4f1b5..57ee72ff178589904f4cfd614ebe9f9921562dff 100755 GIT binary patch delta 700 zcmV;t0z>_-2bTwsi+=?K3=Qz3RSf_D0%b`=K~!ko?U_Aq6G0G$A6u?`ra~qS9|A#v zNJxVy5Rlk)=m?zwkzbHMfGB_tYLGxOC{hX#LQdio6oB~p10#cHck z6qI(>97$vZX?ol?t^(9NB0bHr$2nzxZJ+Ah>k(-<>v2<1>j7+2fOo3*{Z;{dWm5Iu z^8v_+sQwlW)i1oE%7Pw?AY znWrGn^=<;>nSW-udjNT+DYNT}I zRgIM|l4l9Ut}Xq7AdlTEq0J&@7)ls@nYREecV-G)pWcij=-ns?~%yKe8zHF7ZW?(Y3ye$I*j3mgHVm{ac zW?(S9ye$I{xk)jV{BbI4PGlNXY1m5f8Cl)XcTo;IzP4zGSVwJ(aEejBuk$QXc252U z#W!ZH1An!32ur59A5eUQ_DWb|lrI1@DaCxm8_Z`jsoE_8@b>8t!d13jh>Hg2SS^aamB3|W`02haof4*))P zRsTbl`&|XNn=$^r4{7t<;n6Py=rGd&rcl2C0Fxa98x%EEFf=+bGCDCcD=;!TFfbm{ i;sBFO1057KR4_C;F)}(aGb=DMIxsMAk6OHweFHIH(Kev~ delta 778 zcmbQvzK(rDcRdTg3D?<;UoJ8*Fx7avIEG~0dppxML&Q;}VLz`Bx2V7LK@&&r-3xZc z=&m=>izxVFVncKM!oii;r{`~EC+OxA8cR%LzN|p70Vf5~;ZcY8m3y&_#GREgGGGo4Q z_|*hCY0IlN%pOJa>sEH0nA730)-3Z+PmdXw&ZX$a1DE_8pPreNo}1hpCp7DUx$(_A zQ(7Dt>^K`^1m?BL{CLo?(#Ywec>JI3-zMiOz5aA%ZOrGBf98a^JemCE%2i(0_f64z zw$Ev_W?Wn^E^yO?**WXCUA_AJXt(#q%!R6neM~biTok^p_hPX_TabmFT%%prvL6;K zSwA=@zq`dG`0*Cg#E-j}#43Cn*YL*qE1b5F^x|oGw7j9ebbshS&dot$6C)$@uT)Q7 z^KrlGX3-hPr$>D_TofMG=DM4E%VFhUrA_)e_e|NVG&eOFRo>gSMVHO$vG1Y!xMQ~; z@pg9~V5&PH_g*RB@cu%HmaPp!_re@_^VK)>G>P5wOW^d@eEDEwmD!wSv$f()Ka?>U ztYvOrBIg&}7@{ER!5Y*!gC(!!&WCls^wSP}4SmaftmZZU2KE^b67Mh>+k9JTdnE6H zY4eoBdIHCVWS()!tv}KJ__2V+sZ^Udqk4~{OLlKNP^iar{^lX^%?z=Pg6Eje-^i*v zzCzRBROAEA+bd_WzvP^g#-p*JIY02wmcE>G1ws`^{OVk|uLxQk=X}7p`-h}yi1P!c z;|;&xJPrSn5YE`|EyMV3)!ag8WRNi0dVN-jzTQVd20hGx13 zrn&|uA%><_rbbqV2HFNjRt5%zLQ1tL8glbfGSez?Yhdu7br7gQ5@f^VJZ4P*$8}V4 diff --git a/DSView/icons/file.png b/DSView/icons/file.png old mode 100644 new mode 100755 index 2a4715da3891cd71eea4842e56760f7ae488d5de..78c469cc13bf521c2874e3235ab4d0f9ba9c70ab GIT binary patch literal 1886 zcmXxld05iv76(s!RNV{oXYZ&7oQV%h1b_{O>jeU?RUp);#U}XSv{VpHJyUE%(%OL^gd?&& zIqX;l3utIk;MJtVR+Bd+@hpqRWYZXQzz}X_qjv3e_hK-AX3=6|*}!c*g^OA@{!kVr zdOd2%rX^5-{R?_<^#;Da31U#$Nzp6{5XoFyk8k+#I4$vPe6%{s0v^fk7pfVy&Y{sP zI*lF!$dF?>YSa4PJ%@Fc0?e&tP5eLR#Yjk?(AmJeqO?nG(Dhg_jU7(`>Zki{)v$x$ z?HQb0F;x+s8xn$ee&J@o%>Wa&m-x2MTEIm@b@a>Z+kGvr!i%*;vUI>Z`Gw+zc5jix z?j3U^6T;3~DcLRGe=v(g`y3D*RE9Aqt{p=srVuAo!gIs7&%5yS+lXQk*A+&CjVBS5ww%i$mo3b(0nq}ai>64^dNTc_M> z)%5P%XdGH<_&Zfk5f8gpH8~Kh(oXc)Zd;q0W;mI*kH70D|keVu>jNc6)E@aO?a{t6N~&o>pUu`voW+P=c{# zgG9n`>ue1kY~kRVEXpm@Kkt&QFr?0 zHyUjNYrgW7i-n-DuftP4mWAng1{wT<@qLb(NrafXKSW^h8b_)y+W1$@gZ2B8T&Mt!@31ScQ04`$wgRShNreUiwtNwI`iiiXSRRMJr&bnv!keRU(^z=Ai2( zUiypBfSWTM_hCEz8<3HYU0a`U$R_#MWm+^kYzN8qb6#QRc4^nX-%gkxx$j~!*(dY!ij{vE$=YVSjRh+6R*x0rdR;frNmKi!2Dm@o;T?( z&AN|nZ1mt3qz9w@L3fJ=`rbeY!@Mu8oI325z2M$>)bt+c0sG|eOzwvn+Or3u*Eyed z)gSTFhF$*P4e8Waz=z5eLr!f^x_(!jIy)6ZwDt#8Vp=WcFoXnJe%au$ie&CN$V=!* zEjf|*sQ30Eg611v5!h|?dE3J+lLBi-cJ;{%BXnvZsGHf-A!_)IZar(n+;E7S68Ney zYVcp9*U`RqxSW4p@!79-R!yK2kJ)lSC++!PTw(#^yrpC zuVwT-FgiRJ7z6rCLD#M)C|!{g=l{LL9$`SWFhf57(pJb+)isLX*l{Z>2YyVoF; zgtLkRjed|Tw??j355ojlfjz@dpq!J0_Mhpxdy6-`!s+(ga>*o(`Pls){kz6eYVA;p StX8c4MgRitxOdZGQs&004R> z004l5008;`004mK004C`008P>0026e000+ooVrmw00006VoOIv0RI600RN!9r;`8x z010qNS#tmY4#WTe4#WYKD-Ig~000McNliru-U$i`GZ7a@E2jVe164^xK~!ko?U=uA z(@+$~&pC+`2O?MQ&*_F)M~X4%>3){@UXSKyv$JnV32iynNtS`2fH&fGwZf(gK3&jtJNT#P7fR> zA|N7wh^=n7+dexxd*AJLcM^$2DWA_r0ueX^j)(|SsT9o4&cf8x)Ol1~S?Kju(==7n zG#}ZvZD%r>H^;}vUv*vYhQ5E$6A`iB95HjhInj4FP1A1cy8cvAlozV1-m6q9iP!-A zAl%;(u|Fr=P1CfDq9~6|)7+x>Y7Tfy$j^8`Qbf>hx52h;XFQuqrJh-q^#cHQ0H8T`0IzXB^UkNLsz5}*eE#qe z5tyEyR*sI2@&IrvRsgSQw{hoFQ4~lflLM0)0HD!mgt~7ebHKZnc+I=PqE;vXcO>Id zs1ovf){h%DbP0_z=8U_4zbNoU6*vQa(f2dd+Y1fAxb{f#0k{qN^&NT5d%?mgPzh~r zZo=H$+(WP7VOeoE?_Mda0&S-q6^lh-Wl}ZKb_4zoO-{QZFqng}QHK8Hh`vGqAtE zPehbVCX;EyFixEDa=EMlfV(`zr!UNG+qT_aSXc-Y$s3=6|M!2PtK30eR->xq#%CaM z0A^lc<}b`_^x8LO-nw=G%&al2#8X)o# z5|LFAX)yDOd)z4}cebtt03vRSi1cdR7;)Sy0C2_SM7EjxhkgP;82~;b;#!m!9@je11b{UWDL4}#sz>Y4z0hPO9+M^j iaclBE5Vs}(aqBnxb}VBr1CCGt0000ezy`AbKzYwjL4e{?JsB#oao!lE83lQDU442>-ivR9 z31Yj&v!mjeKz*|aD0R%GlnCD}Otj#)^yymqnRCC)m!HF?5Fm z!k^Vemj^uPV_%t$h@ww=EC|CmQyY%H112G&J|12R+GiJhXcW5__=ve;Y( z_BcPhcYo{Fv%^pu0WBcv z8ao+RuhjO5U{%w1?#L~lsdN&}LYor)6zf@Bd|M<9Z}Oo2Ti~2p(KbF!b^1GfaEkh8 z$H3YMr@wFFl4E;E#~5L)R9jpma0GU_OO`-OoEJjk!;AGG&#rD zEt$$oc0P%Pn0Clj^8z^0-2!y}b(^bNyN`+Y5s~}kKg%_YEM+%t)!n>%EJPGTo*J#- z8t_=$TND&toZWcQGMD@KKiL%vh>>K_n#8=AXo%7D1%qa8oM z`@7~s4+)o&3Wc2!&i!OlDnxiVOKp2w?4 z;~)RtrFQLA#&7wDAxq}{+|>I$B+3@|FHllslK8WgIF^Fa)bo1fmBezIFyQ&{HYlj$ z;ugEojB&w!sqM1*P{|^%mKPYIw;jf&#=9lIbwUT2p)sIaXwBW6as^%}3cF>{JqL!jc1Irc77E7OLaYtiR+0TS-Z|SL{qa_{~T%)+5KH(!ZnWDu~MOckukoo=(9uwV3n?1U5R8DC z3A+P<*oU!rlv2`5QE{FpqvM?=i8}W4J^0}}HS>6!{nmvxfw#QkTwyZW|LSW`*-Zi@ zA6JDn9(~L#PGX@&la>*Yu&-dqAH;fD@v1_h7hAPXxNR8r2TFUN7DbhZ zb!dfxue&CWnb=kpyVnK44_1afv)|L%GNNaH9)fzS^F-wldNy)PSZnd(JlfVSw{Wh8 zV)z(_(hmHWk38Mn4c)5KnhiQ_#Qnh^r;B6d@CL($oo4tyB(D$9GT#Jtz|2D$2I9_( g7SgA`g2fxU6<6=-wb3004R> z004l5008;`004mK004C`008P>0026e000+ooVrmw00006VoOIv0RI600RN!9r;`8x z010qNS#tmY4#WTe4#WYKD-Ig~000McNliru-U$;505pexJe&Xk0pdwSK~!ko?U+GJ z8$lF+zht-7*kaFxcrrghc(K2rz4T_VJqRu0A&CA2({qcL9(pK+UW<@RFaCni2l)rR zS;4DTgbI%9AtS61(C$urS)A{Z<*~`UZ{Ez?$=0$gqsr8+6Nm!V2}A+w1j>>Y`I4$W z2EGGtZn^cyIrq64oi zhQnd7aqnvn%mUAxb8En+bFQVTNtFbyc>=5h+dvQri!Rgv-T_@;v8;qu8*KpZfh{jl zI1kI`z*ArYSoPPH699MwECF*>6(~&gg<11^Ab5h*m2 zm9N0Vd=5+#l@|!!`&4xq_zJxAp#ktsL_SQFfbTNi?U09agH00000NkvXXu0mjf$s`0E diff --git a/DSView/icons/instant.png b/DSView/icons/instant.png old mode 100644 new mode 100755 index 2aa5c250095bba38be002808e05e6f496061278b..c3e249d3732fe960f037ce142a99a624616727a9 GIT binary patch literal 2371 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 Yy|g`#_(LBCB)0;9c5!!ZbPB)vH*2a};s5{u literal 1081 zcmV-91jhS`P)004R> z004l5008;`004mK004C`008P>0026e000+ooVrmw00006VoOIv0RI600RN!9r;`8x z010qNS#tmY4#WTe4#WYKD-Ig~000McNliru-U$i`AQ5(`YBc}=1DZ)hK~!ko?O07n zTvr%9-!)Go8DEKrN|_{G*+{cVApsX@S7uc~#AGu{YlEA1T8X-`SxmBtpdi78i^9Wz zx=|#!3$+oFK$0#}T8PDMi`Y_WoN9?N9naTA-h`*j--FNRD zQB@pfn4kre3zlci)f=1p3 zYHMqcBoc|U!^6Wh<>lqR`7JFiRw9x3FcOIz55$0olpABN^!4@i#$vIfezRsFrt-_Ls^tE;Q4Bzaq1ko^h`V+pG#+gzyEQ0|3z8-hN_rb#;>D zOOp2n2L~H-A|4b20MOdndWz(=k&%&e6%`dZUuOL35kI01BHrHnz(K*GH<0!)SL{Eq z{2-#sgX(6e+UyvL_t(|ob8-#h#glM z$A8K}1))tOG-Z`dONa>y+X~h+AMAdpZW5tJOK<}*>xXkDId|^dy)$#>?oDKVFx;6l zbDnda|Gb>{f7`RLus}_DZt8XhZggq^BdG<9q!uueTEIwZ0VAmejHDJYl4VPspqbqS zd>D8?&{L7w1>gek4@p-lvYCsXPPRnMYz6QYU<|koSOt89&fU1nz&T(JcndfTye#Rx z4y9MUfM#|xV8CWz9k9I81d4MB_#JQ%kfi@sX}nSaf6Z(R*advLGNaY?YKMH&T81>r+T12@BY?1V0OuA75n%QH(6J(dBQ@|czx@xiV;zeBxJP6#K zamfPke*;NRl})3xfM)hQa9_r$8DNK`sj`{s1ZH+OFbRAv`p6r?lxw2C-7Suf9t950iTz2Ca;~MErLBXiU+W*sCd5G zGYO0k-<PV ze^SAZMAYw|0NyDXH_Za@3h?VPk-ZO(q=MIYpWBkF5HYi#0%1Mrl%(O@xy-hCGw?~` zzk&aj^!G>`nAu5SC*_-kLg&Zl;;pcHr4qI|1J@v+qlKF4E2vjRK~9Zz9htyef4ZReGA) za$F_4Y9_ty7}CC_eQ%~c?3*1+wL;659ZENe;Xvt09$aqDtTGJ4N1;al@IUJU8&%8ubp{(UV(k|QpeLD{LYpW*h z)1f>wz4F!r)18$=X7;t5af`5x#qatF8v7+H!TU0l3eMF*n|oBw%udkQe=B_kxV-~Y zG*$wqXzrz%1%@P5+KTbB7yb;a4uXF#sclyl6l;k*lsaAwJXs)qF1$eNfH#8RH-L3j zi|2WgM)9E8*On9=7d}Mu$XrKyr+rU&GEMHyPRu;b=!FJ2EGhS}W<9VTIj~-+Gr=1k z$4!GsSPo42=@K^tKCe?We~Nbn7%w6ol$@RAC*oxFs5}ikR#CgOa?Gp;H>jT;#sqjs z(w?F=>*;Nd($m}O36|ZOV3|-)@vSGBM^P_xh@@U<6G^?)E0T^1m_@zVu%>}}y>KM8 zfRWS!Mp6qHNiAR`wSbWnuF#s9e=;$%fu%^NY?@2{ zwnCR`>ADpV*w}?MhbXvepb`PiY$xz#;B{aEcn%oE zEhapM>q5E^T;B_P&&L@CP6Dq2dwskef%)}jHVq(Yt&h7A_&RPr->^^PbVfQ!K3)Ni z;yyitxOVeVU_#Qof4_Db7zpA{``t-NM_c(`)f4z}+|tzn|2r6*8^Adkjz#Rlb$2C< z_-l6KL>URr^$|>blr(QZmvz<@uRqLe~g6WI1Jkk+$yQZ$KUS5 z`+=Pq`5VSf4H@@&OPX&ktpYp>Ey;4gVZG_zrxU>k9T*8R9wEq47%DZ18_le8DN7Hz@@_0d?cB;xm{ zqTY*pnlAurB<=N2SLV$sOWrr4D*~F?Ab_uO>pN^L4H1;B>h7=9vHO#l7CijQLlC@R=LFZD{s%e36`kgZ1BL(q N002ovPDHLkV1m8!8=3$B delta 1065 zcmV+^1lIe=67LB~iBL{Q4GJ0x0000DNk~Le0000m0000m2nGNE09OL}hX4Qo32;bR za{vGi!~g&e!~vBn4jTXf00(qQO+^Ra1`!Vy4!9*A?U6~=e*?NnL_t(&f$djKXk1ko zJ;!9EWhT?;2#Wcs%Xt=p3My5EEE6)W@=GMKSeh&ng)~hFnZ=uq#6?0uaFt+oT}ZkR zvdBWn!iX6mGoYclch+sF-9&5`(n8bYB5%s5Nt&7W#<4gDF5G!@&-uRdzWdFcOG$W^ z@m#eN@Qa#(e`=#Tk9UUQK z%v;^v-7l1z1K`5|$>&L492y$BJT*0Ss#Lz~x*w8se@VVvZVrIIEhGRD029N*!&g(O z)Qcw}4GavN&gF6!8XFtG1n`QdQ#O46jFLQ6Utd4g-`^ivSXlT1z;DN`cU|`r0J9`t zmGrCM_<@`QB*T(M5{X2lt*z}cfXDdjn^jj=S7>W%>!U~{@;OOg`@R@{3<$swo;$d;Ct@{5a$R{(5!$$=gUYl`Ope|}kCU!QSZ_ea06fdDE101XWd?CI$_ zH#Rml7Y>KJ3Y92^q(=bE>gwu+`T6X3fdYDadtXbZ(_iCYzdteIRVK5x zwl+ICIr&3i0X^|R$Kc@Lxm+&ytuf}IwN}QMN88)m8|ifVomeau_?;kd1Co$PBw8|= zf6R4b%mZW0`t78A%3!D``Sf zDjtv1ah$7;iLQ|R$xAYGoMtJxlx5)Wa2#jXahz5FeIzFejYl%-ZLcVRW=T7RSNTbF zi}+7P*#;yvTWfFS^LgQ*55998Cj(%|+YT!v&jVN{nId`fgwjv=`Fy_jX^U(BMyrjQ jfoh{>pxUSzs5brtTYZ#yD5qOq00000NkvXXu0mjfUP0?Y diff --git a/DSView/icons/math.png b/DSView/icons/math.png index 59f7c5f4e46090901387ae3166e7cb87f4af3e33..dbb83a78fec272e7b6a5b1b20f0431491b6a66af 100755 GIT binary patch delta 5444 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 diff --git a/DSView/icons/math_dis.png b/DSView/icons/math_dis.png index 4021208564632738ae309a6c0bae7445f7b7b8a8..9850dd5e09f1ac5da0cd70726cc86e942c89cc38 100755 GIT binary patch delta 3345 zcmV+s4es)h7lj;1Qik-J~Bxe8JQxp8f9iuIvgW2hgs?^dzgD>U?s?w5<>5Hr7-yVw_FjAKwZ1*qT64}d=gwraS!(Dyf9iI0 z19o-P2uz3?feBF~Fd=FLCPavs$HjgSS_xqHQ=R67SY01JQ@W1o+fx#nKr z*3j5u=nLEf{G`%`m|1V&N}xCJb$9>1yUzm71f~PyfXmCwlatuCFAIQvm9_z@ColsH z0S3fAJJ1etqY%CX0|z$s{`u8`8Kq=_XN6?sjD3r4IG3BdZtAAEbut+3E-fnrlz{)=H|Ls z*=(#0T}B!NPD2t=tTDrp6#N{Kl>s&ZuK-U2{{ap~L-Ii2PBeGLf6)zi8@L`A1q=p$ z1MCl66z&~KQ}lr5q7}LgIJmjFc`x8x;7GJsw1)d7LK*}PLXFJV^RlQjJy3^+lI!F$ z;89>D(gB|Yt_Chdor4?tv_lfyg%g5^#XKUmn#J%1s4|w!IRhL0~zvCt4iZqXTAkK5!Gz3-|}p)s92t zJD6cN!ZR#r0ScS`Jf9@G_^HStF`3~?7Qgo+; z?*`y&W;V1$U5BEfI1Bh+iE>#);88$xxYsx{%eQnA_kVlpfvslN6ZkBedv0;}o85gm za0?nrV}KJ2UEI{vR7Xx7nd9!I)S?b#BYyyR3E8+O1H)42$2Qn*1O^YsFyQ0JaKA6_ z+>FqkqYF6zf0~<{_d_botB90x$RMeH4XHD|fn$^PqBFJ^f!RRHC}gz%30RU>W;vSI z#sdcwsAD)L922h z)9VnV63uh>byc=$K3a`W047(^-inb1ft$*lZ%1=de*))EjF`3nM|hW;^pp#9*>67Q(+V8 z++8Re<0+Y^x z9cU#y3itxr+|I{E$ZvPMyZ^h=irsw$T10kgq#=QKA_wUuW;QB*!pz2?1z(E5J5N&SXVP=;hz3*70;tT>VaQAN3FSYGR`z0+$Pm=O(k`zQ51bz#+-`(4R>&)y%q@!(g_pF)y5=r9m z$e7Q!SlW<8--A}_v(TncAqm`qBx(SfTi+e4TPa&0e*G*;nJ!Jk(Q{egBfJCWi9^+E2g9%eSa%>5dHhUVtx zQ%kI(^}t?0zcTe5Rpwr0e~|`(SA|bGJXT?Sf2*+EgDKb7CDi7?gy;%^iFK!)7*+SJ zD+DIqOWlc4b>FI;16z=r>b)w39mpQ97`uF~+>Sh_fY#rv~5P6%Be*{LEnq9%sXy17n+P;hv$UIj^>>#>cA2tSz}^JGlwD%@FEmTQ-_9re>@%k43x(87)oco8%3qw z69|eh9k?R)|H5$1@6dyc58J)b+dbx>fb&Oy8D=&vQI7-CA~1TFOdaq73hMx+ZS@Li zS|@~K3sDp*h`>E^)i4Nn6A^a}irU|ZLVtf`X8VV7Gf)6#gas(u^DeZhA0JthflEe>}#gOGul*EUc{q5H?b1=kH0B*th}s zrHkhCoh$4?-qYV?UJF|z}K>rfz4L#Qk6 zyp6ZFTan-ScquQ2ra5-aP7~VEfQDonkO?osiK6i*qT$;C%nfO(o5@K_f7q(m-GC$y z^a_CSe`x4MC>5Yy4Eg7_#E$Rq2-OHtoLyv|f%4wYLFz*mMQBB@DcMABk2sRRQ7GmQ z;Cgqz7QoCRRRy-)vsfSd-G}1H*8@{^prknmf{2N%irFYb=?K(`CGNf;oZC!p2sgyO zquh_dVTJ62qFy8PHnU7fm58Uje#Fe?p}F@(f41YsS6WYFaIC&IfRGfkG*%}5lAIQD zos5!nmH|;5^|?sbn~7c+5iJ-4$VpT@@p=ME8oUxcgt#e}KhH&dt|x{5r$yiCEO#QLHH4#g9;`>NIEgR|DY+eaFG5_4tcR_RgfJ}cGqdNATdW;e z>Fz7c>|Uf~zeX+xxD9x~%$AUQ7tG6uycdvXXt%uffRyv6Lp`gKcD%N&nGiLXenQj; bOo;yjyK46lM*iYl00000NkvXXu0mjfi=jb* delta 2713 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 prf@M5!Uql}ZYTW&{o5p$cVgRHOyI zy{D6rAs&C;2?_}s8pP9W!~g&T(Md!>RA}Dqm`!h6RS<@s89U%m;S{NAnqUGFLPCmQ z0W4C)8dxB9$sg#FRS`QD2@+CQ@DK6_3JZ295$mQK7D!FR0wH0{S1O{^3XbFC&db8b z$*q+y*Dh|5U+L-|oqO)gyz_DAoNJP}$naqsFd~02Yy(CFhHb!zz_1M%5g33B+u7Nf zlvI)QG=QWjNpCe8jpy^aB)!&ZwO$`20UJOkE=9zunO%#BWq02LJ_4=*zhznWsia?u z#o{eVZvp{)V`d+lS-o!tlKQ$LVmu=Ll%$Ags0$HsKO*jT&mSBdeA2ION&TmgnPtFM z8jpWV`n=U@y%7;#r@1SV>XP2cvg}4g?5Ft;fWputAZaWjzL%sd%YK%0B}L}LToi6e zqFSwvN5l_l?k7p5p+_JOci#dIl7}0)M=gLtp~0Er3+he*k|2RY@|l zM^JZ!K{PT0CTSOC1K6b9tV+#ha|)nTDs2G|lD?~Ec1hBnnf*?mwJ-<*Lq)GO;F4Yh zW*9^hIzj=MX*QcL^=ebE5txXGyTGoQy>Dily}W!9BwYo*m2}D7=YZ|=zS~m~ODcc3 z`>T@vF|++a7!P1(`;uO9_h%&)dMWIl2$-29HGq9dF9UyBpPv!6Q(zLvB<<2;QTG}F z1Ki9m1K&kN*41$ziG#ZrfC))^9Chl!6EL$((vOm!1IGIGe$FwXo%h>mcYA)DdMsiG zD$QoISSppGmlw}Mv)S}gsdNC;X!n1S$3ozV!-e0>9oE2r()m9@K5H&~1pZ$HB4RO} zR~Jv4dy?=F0W+IT!}lVh91#^@Ued3_HuPKs03`P$T+MA-cB`u~XE$5;%0ele=6-hTGEt#3Q`)6l))66#9ea78ur|3F80?GIl z;D)3*U^RKOB5BUdmeNu1j+s?}S>U#r-IX*UX)PybA;~#c4M|$>MCk-wFPurh%r<~k zGb__>>+9)JIT`hp9pQ3%UP*e&%fO+R+`Z=RGr)qG-Rz{+IZ#i!AJXOSYuyX;1oFz) zC2asJ?p{yE)qw>`bNRe>_gV_|l9}Dh$vezp;5M-8?rUA^A9VC4hjoY_tLxab>vXkI oCms?;1cr^#d0^NW5g0c91v1xsysFthJOBUy07*qoM6N<$g3}lE>i_@% diff --git a/DSView/icons/measure_dis.png b/DSView/icons/measure_dis.png old mode 100644 new mode 100755 index 8510560a6824e086cc832dd7025d2c29f4e18b40..3f152eee474dd2a7e85cbf21166381409004c8d9 GIT binary patch literal 4137 zcmZXXc{tQ-*vH3M#y(j}9m`mT4ziuGWeHjGL)K81B4-BKW-^ws4iZUZtiy{EFH-??;qdizOK*r`#jhE$NgN_^W3wyy(GZ<2QLT&5&$gC z9FMF0Z*g-SuNgwDhU3D6w!9V!0`ZCb78|ILbov-Vu)t+=2!oRYdJ3V%rT6PUWPTNE z5{C5)xecmmq*)##e(W(aLx%d?_D5s=LxMpfJTh{}ocA|34MF2>`}_J~LG@6Y-Z2&Y zkKRUl|Bgyy{ezGot!*gJvBUG*aSB0UBfM`TL7wQN-*LYG4)+iB3G_aW-UhW&n=_9w z_iya#eLL7c*cU_<|2O5BLH>7t*liyq=*Lmg^8dw5LxO^k!C25wT3+Wd_mhq5xp|XjSu5zkKbmPn zRX^}4(O)ON>sTLam-9Y9>=rSN8y+)Ue6hDe-oCJC>vZD=>~%FvE*uL959b%rwOrA= zB2YArp}wGkflR?qq2Zc$9l5B z!4d3y@`j=V55x6;4U?_ESu&XR>yy+m zd@d?HCTW+IQRcvr0o_Kvhgn`tSbi~y550J3IQt5Q2Cb<*WeWzmtfa%W)2K6i+l$=? zIW}>-(d>Ui zd1msO0ed~T1-wlh&-s|72tl-+rf^WM62AXoF6k$l;ES-AjFI**|17d|uyv_PD4)$~ z!yF~T1lh7;QQFTX?xD2B3%FuVQ}nB|CYL3wFZ<;P8nkBUU9yPpgy@Q1z1)B4`hB}! zZ>X)bz?;|Q655xjm*fw)cTS*R5Q{x8mE7tr<1HFUr{=+OfjUC+K|~R<5IuX zzwOBXL{$JfsAS6HSVMJrI~LRH&0E?^D2SlV_<*wn>dnDMu*TDpl*577H-QkVDB+zr z6C$)={PVYM%NPw@V!{WU2vcs#a9?XOJEcN}N~B^?;eh2N-g<7z%R)=yx$W#UtzNzZ zzxgi5)1DcTuD}qX1hn!sMHFTsir<3H&i?xz#46Ks$B46byhf#$aHP*_t~xyv_R0y8 zRJHQu=EU1}mQnEC4vt82=?apP5pDEd#Mcb|NRX7At#dxJy?SWW{--XY2#MO8Q)8A4 zKpQT6v|?;;EFmPvoXk&4V+hx)7NCpXGVdQ)4}&;_i>eb15N+eML*0kE?7Bi%A?I?j zmx+sG58QZS0LQ8dTLzSOV2jYn%5%4I!;hf>34D{6sO=>GD~_pPln8#rSR5T2$zz)6 z#@(*$Q7{ezzqqS{Zmqb15=pY#!i3jY=m>q{)m8Xxk22HqLjC4cMvr%u|2ljm)0wm5yzsfSRkV9u2jCE9r8=>bj*PA*th2w zR5%X#BFAa3f>0$|WR2WQSH-_?nAvt8j2grs#Cy|ZmQI$ErK59*tFSDc^K!U%7(2K; z#*Sfu(AU_%o=~J`EkBsOC5z@pU+mKti&VAQPEv-F^*$OT53ZnCtDUV0rtpp{j}QN5 z81X(^e*^Rp@<$C%&J=uq*sOR0qlU&&)|BWt?|*H&Os}5>Z{n5Z4=O$4l^-jB2Lqu~ zF5xiitJ(DMdBGok6wuR(A?#Mm<$0ACJEyJMQrVRR(uVfVXF@^|DC= z+o!U=clW6Se@x)Gbnot0Z`m;Dq^)bHmXeBVgf+g#g{T;-r`lu)CcjBMQh261N;uM% z-V*3z0xJndjm>WK_WvM#ZH(eA8c(1O_vcU4!A}yka6!``2|ItI^Syi&)#>rPHA7e%44rDP~Nm8dmewCf6r_d$tS@4B>*DmoG5 z1H3+M<=Y8b1Ml;1)Uu^EH+m>1&ios-{P^Z<)W5?k4+iqCu_U{US-dheu5=yd<$80f zFk+dv2Za$*<=5hrtS#(Dg=;dC$^wD=b+KMen?oQ4cJfBH`?dDub;aS_=0V!ew?QvD zn43CbFMLtqpjX@bnrMrRLS11akk<3QqJc6u9)n8h1Lu9_-_EK1d~41lA7|KgM#VU$ zTTkX^yzFOx*enEXl6nxr@ZE$#f_uYI&eeI;vYHl4`qMgp>4y;t@~nqjK2y^JuRZp$ z%PKpk7N1#gaFt!ILJXb@d0qk~**Ew*sU(Pp(jWCU{7Osh+e2sg81dauBu5IsXqWK% z;%A(*w$4eZwd;K*V7_cpUbR;gw$#e2N9TPNVkG5GX;)iH5#mypUyqe9sNm84djs}& zD&P%Wtu|_X{nt6L1>=2It5>WUUq)u2b24=XHD~Zv3r`+yoo1?mDW+^9^QELP>2Gu4?*r24AjF{T#Mwcg?<*es`0VQ zJ_Z@j6W@Qptsesg!5TgqKfjBO`nK^GaO8?C4xU=kJ!Nz*C#8W}MGJ%hI`hHfxE-bT zL0Vj5_W7L8kRAABh^ayPrGa;4r@*i(u1r({t!3r2+U=*sf4aoe zk>X?dQp6|$9l=eWNrLD&6}NJK7S{eq{`0Tz;>Cx59X@fsb_M&D7APgvAKBvQb8_NL zrt<3HQXoo<&2@GpC%)^(s3jwXIx`3R?vVk#1p|sv;WAAo^nnu|u_DmwN6G8`G0>Ut ztFVb%Rn4x5Ge+EJb_*)Xl1h!k>X=Y2YpFoBd$T>%^SU@4 zMv~#_nYW;s9$2!PtPj)hi)f13wubHqBa1YBao5ja=V9f#Fx~y4)Q)UxNM%Q%{R57} zjEWnaJW;h%l@3~|@C|{fz@=`<{gEB%uFkpu-mws-Sn*+T9&ohA_m#ZlU+QH>+*%2p z(=Nnk<-(gL{Ahj#-ECU@yK#&WFSSqUCrClrTOFl~g4Ds{ekx=`@qFE*7I6V*-4ai0 zvnY;6JN}vQDaqE{S4j|#Q7a`qp%A=QjM&ry6wR%6?@lkdP={@o& z*|jG&JwcUHQ_t+4Ht3ElY4IM}uGxEJJ$(bXD=a0OWFS*Yvu5~U+|6?k)$RLhgA~5c zBA#jjUorP1gawXn9!%f$a8;$aiJ1e%1HTMrXeiKp_wXR0F(5`PzRh8QuXXl@#1q|| z{dtM8xeuodBAJbHO??l8`}ZH^P51vr4!2tP`U&S+no7M|`x0||0)eF4iPCEh8sSZT zyl?3(T$v-q=Bdk^{-jf^Haw62%l$UN?sXS~@A_y;#2j!OmVor_2i05fynLz&%XO^;6CRM@FhS`@Mx?~SHXyY2R0>vFlCuy`iI1KdZkA-J`V5%T+;XMFldg868y*CVIG!6 z*8N=jL}J)HUl+#|TvzFwL=8?|?!Koa;S;Z8C0|8_r}zdB8D!g|gxOs;(zqQjTQZ=2}1qeY zy{D6rAs&C;2@?tcI$*~Un*aap(k_TuwFt(bmMLvZPn#gp+C`>FMUqB-q~mLG&**g;WuEik zDZK;3yL0B=bI$*qdp_O-VHmQTsV->)QUFOCkOF^5+JF>5(gytZ0QS@>Kve_aD9`|W z7m+1ZZ2?+=X%U&SeRV(w5QxaTxBvvTc4AfC2MhzpfjqDT+_3Lcz%Af2a0fUCtOC!0 zn<6q-D}b6K00h7(;24kt8ol360}pL|#N+n>AA!5IDvu)violGm8-d%vS>UN>_#AM- z3}1gX{uNcN-w6OhV8Pa(fCnP-7I^IKDTv4`;0f@-cv)QDBo4r*8DPcM^B(_?Jp)`c z0QSeBKW+eZp3GnqJtclDbNsIF191V^TlOrM?N_^o3VXLrag_zzf_g(;SEKM$N!8lbwljFlr9e180EWtd9h7&@yn+ z929B1hzcOYCF(M8LRG)Av0-h8Lfjxs|HNf{MDfV*~wfS0B;4gmE)6Rt8vk{5p! zKoK_vAI3GLT8;NX{IpM`!Vnd}FW{=3tGZHDNp-lM66qyp*Y|;Rm`j>c07=sZ0IHf( z)lgM)Tk&=hm6y>DdtOr20o+{D1$;@`&}spI`*W((z+`cCc@pSQ)hzHDH<1E#i^!lw zr_Xq^z-18`QPnP>VEjH2>Gx#hfdPNq0Eee51DrSBKf20|UAsc6nlT}>Cg`Ghe~9(w zav#WxNMK5GW>DUXQWJ2`_`PO$AR_%$ICNY@+C^k?3tg1~cr<55_gEx`Jr8vPeX1Io zcd{0#0x)XEk9cyrfu0CCG-l4+Ojld^fl60s+^$Tot;dX?F{1`Wq}z*J)`W8p*`BPO z$2{3?SLu; zJXipRMP#IG|E#BX?M23P{-diBfOHm0noo3!(v864rO=>b1Gw@oaTItjbUMlQVe4;hxmpt;+YB+Q6auc^ zh!ClS^k|8Z(-S?;hkkl~uix*F&wahF_w~81*Y&#py6-e+M=KHGgTfF9M8w7#=du@_ ze@jqc@61FlKiLaHq1GPZ5C~l2w|F3T@}>76EW*aY0>A`4=KDJ|kc z2o+LZ*=N0n_C@R=oPW3gy4vXs;slyW$!W5D`hH`a{ly{`zf)|2sS|+%Jf{XQe_K*tJ=ESnxM? zCsQebl=Bd_EH`D3!T#G%qx$(nR(>VU{m*6|5*+MLiGZy3<*@dcz;7EqFe1nwQnvW+ z$lm_RHaJs!RQ^JKOvWLCWS4Pp;Ap~>B7bgCm5Lbxxlp-0(_2OGvr8ftzd8%AwJfqI zDK{H2tA;~^6;0rAF3E0(RgOCI_;m`Z92PS#7Q5A6L%<8~-dsLP@tWD}I#Cl7U&HF^ z+vcu$S#LV%8M1C0u8nW>pIFO}S!_C9)ZBdcWOpX8^B0h1uqsAW`oF-b075W-KhzPL z3Ekx};`ywILJW|Avl_*_3!GgK#(n1%}B z?FHs=#m=q>^)){qPA@wW5c(%us_Q4a!D7;hzNT0wTrmr=pQRLQ(VMq_T`_@GxeyTL z3(*!W{B<7XK_7c@5q%M!uD8lmfjg@*FPO0cl|W9vKTAqfC8 z?u}^?K}#{;&{q}B)Sv{~;m~8DxU^p01mo21SPqq8L&`aj25zbb*&C{wE=2-#_H(08 zn#r}|Sn2iH+n2U@8p>yN*qA)!%Nup?fszyDKeG~agnELzbp7ckGpv$o`4%mf8rPgS zD#i+s45%Bg67HO0r?TTKWu4<`paH+@7!QH*0@lZ$!{~;2jM-h?Yk=-5Y`>6Nm(x?Q zkcK~Y8$zOKX-sppr;_35DL5} zQ-E@s5Ju~tn2-~YD=hNaIx^d8PRQbOBYU?evx5lo`K6ime2i|<3^xBi*-?5``)>68 z1PWmd881J0baN+knHl-zpn=ir<5+qxVDs`jb0qYp^zDZ_sCWFNG^C8f*HT_}pkRb_ z0za*jmmVG=a9}zvIwivvtI+68Ks>>BKLUEIH^N&A@ZZimd_^Ni)2^q4(Y&YRPvoAp zkxOa9S~aJKpNKn?pJ=CBCtp)#qb(F8^QJ@-oc%h^!qTH;uPceM<%c%dLwS<`v+>|v zPN9`fnoo!IcCFpn&z&Amz@@{TUyI?b1=l<_9z>It-FSU8!oS$PJg(eU)ZgE)J7srv zq>8*>IN1s?%>ykdT}J>I0Nch=EixgWgp=DIqNwrrokx;wrhPewecP+Z?cV&(Lxb!U z>9oMZ>~6FBKLYMMW2>Bx$|2Cu7x=IDR8Y(IcPx)}^7g*e7%>VuDCtpy$%{EI79KjV zk3yHr(?n^3v;KLLBo$O-Ox#1@ZndDZ+dSWHvd6Nm)T)MWM(Q@kVX8@7un07tQPFW} zu4AL^OLC#_uX-tTaT~OFtr*?m-UHG>kHO>?-A0*4_Q{Nn?gEpUG@_5KPTHj^)h}W} ztH&^nQ>QqxgB?DEF0$KlO;cp0?l{yD$?SSJPRzAXiBF#*pO z$kC%>oX#~>Yl=(1oUZz*s*x5$9?bs&^BX75le7C`ezu)qZ=jY7(fY%;OlCaHzju7J zen3k%1V&NImd=!YrS;cQPi2XSK1t?ujho9FjhvN1GOaeqf@Nm(G}fIl{no7>RaiP~ zrl4yARccHRSNVbAbfKtOg#+yb8%AV8SAyi;Z7WFXWtcz+JJMXxXpS{E1-6*}Js)sH ziWSQ@!WSf*lIc0<3^o5SNbnH&Y!>3{tM4qv91f?z%5>_K)MsZvbZ3ScSaeb20v1@@ zOQaqyw^8ix$l5CCVhww{T3U8mC>bqw2OSi2ouRz$Do(Exy_d&dH;v;i2m-VSLJs-j znAW5zFMedGUSZP0&zr@B(f)Y*WWjb;iGCox+YmG$5>mLN__H6bvkjWn zqe_>dL@MP7a@NdO{>&`}VcTU``TPCf1}60Pmq;WO>468eRxI^@zI;%@7MWPwV}36-+pC> zM3QoqF-g+Lxar`!P@GDbT9vZ7@R^Gr?0uC;x2XI^v`8Myvqlm$H4$wz200?=UgBwr z98G3M@yFTZUt{LYX#!%myG#ftd0fYgL@c>w3@ah|c~;-A>l;>C>|U~%ED;}eRqAytQOx@#nLW&Jl%(P|E^==4 zL$*79%J$co_k?Gy#e&`d;<1d!E0dShGpHb*D}we?%o^OeV>zo^&HHIwDZ&zgU6J5} zKdL88Bxz)63q2DXVFZIbu4$KW=aN(ZnYZ}4KkYWVMrgM8Bsei9yIA2ekDu=xPO9~4 z6*z6g+BsfzAMb&r^RGMHQmc5bhT(W{hSdB6%4df!1u8$`6Ky-jRSJ3~Ro+WJKIIxN z(X1v@En_ENo)ib9XLAX;W;xgOv|y~efNgbSz@}&WgEHHmRf+bYD2@6>Qeg8i(YN%H z`^9sk7iv_o81Y{80xw|)`$LkkR)>k;vBr(R(mSI0fSS32&R*D^PefZz21ZA!iSQfX zS8o0g0cQt^o|OF0*R&vM-IviEZP}x_aR&R>Uz~R=HgmIzRtr^cRrtjMwsck$7*mQ} zky0yL&_BU?vMf+KsDHHvZucx!+o7Ot9`7l+eM)*=lp9ZTw)fVT+MZwIKa8Xw9OH`+ z<(ei*?~}`|@Au@kZTX3YHzAxLEvd=*i#_4Hw_&k)x36B0f5TR1-$pG*-HE;^S_aOB z5qmy`v32q~x8QnV@g}&5kWF)5gozw)tU#0n?jA_BJ2WZIT%4bm!}|B!Z2eC2xC}8o z*tAf~`jcVu1&tLB_YsBM%GfkKumQ}XB2WUx;|GR&D9WJCt)Y8MO$$6 z%}EWas51wnA#asYUWxA)M&P_Lz#$DNVTC-e$TnapxV{e|58hTasQ~LYH2XjNhzpw2 zz8c`XEd*djR;x78raR_|ARjoZ9Q}F>k}t(92H`qGcT$m~fdAxqQ@Ew~q10}vj1LHT zFcSbbFa#R|;C}iJd4IP_q7Ba!PL1lNgaqdf4E~EP(vPr^qTCk+Y(RnBUIXdyl zCFvsHwMDa%tW!y2b9PPAm;6}qvT_ah@8%~m1T*ztRi1Py*0G(tvp8S(~h3_tQ$*jh-l~fL>D2PXmL5N8gm)l;6rS z#@#ADb+H&6sbf^*kGWx`V}7i5T59T#ANM`gR(g?{ua`F4ITr#RdEun|{Jyl!?C-p$ zavH0?{p{6~#e2ko`So$)s7u~L;wjL8;oYXspK~(yty#N| z0PxfP>jj!(zb3|tP%kH(g<(&zgl4S(c=FSs(j X7DtAJ6SVfeC=eS9M_i?u@74bRHuhQm literal 1210 zcmV;r1V#IaP)004R> z004l5008;`004mK004C`008P>0026e000+ooVrmw00006VoOIv0RI600RN!9r;`8x z010qNS#tmY4#WTe4#WYKD-Ig~000McNliru-U$i`8WzoEA@2YH1RF_2K~!ko?O4xi zTvZhQzE_jRmQE12W9+6P5v&%WOf3{CDilEwrgkM35kYj3jXQ~T(Yh;w3k#)xz>F@W zx+tg{F}Nw+L<=qy+p$>1HcV{OwDx>1=G^Aa?MyQ7O)_I(4qV=y^X@(G`_7O1V<-um zS+ZdR7!epYfDwUV0~iq)Hh`fKkTfCbxTI5(c6Au?sHBg)?d&jrm!wkwP5^kYFat?N z+jcWsR7(z|@6BwVncXzAB}val`a@>+shOSixiYgeu6rymjt2^G9+NZ);2{8a-3br- z?`4uVeVhTf1Avl@WSCze`BrR@P~^gDoQlJ81dB6&>GKL9S~Mw55r@D1v!nJq*c(UO_{VPilCA*Q z;pZesT6Eow4&(bG)fWLtW1bo@h_3)#0I)~WX|xLc9kiC&D;Py90T zThNod4&b_^zW|(Usdx^+)x^YQC-^tXTSakj^xx=^r7eXvYN=aKNi$~CM_|JUm|4Zl z=FO};Z~|sFo1BKLJpe8{;0gecER!q)cw{39w4JRZ@CyNmq-rlD+~b$Ryg}fVg!Hmf zGP5~JwFp?7&OLo8xvb12x~jJYC)PH{%x1gTS}?OpBpeRvaCt6 zA*pO;rD$y35A<~aRRC{Fx-4lQ$$Db5M)C(Af0*P<5~;eRNl8Ben3Xgm=>w9?vh3Ju z5lIO&0W?TH5l28$nPel&vS#FoK(Y>?>^HOf#P#=_BB|l}a%?L&iewYOVE}J*iomJ_ z9N;+sFGR)oBFTyaje!M{RR9f1O_HS;n1gF`N_ZcLbOe=UjV#Nix2Q6zA@uriu)9C0dGz1yl2Zk*p0>hU7 Y0I{Y*zBC()R{#J207*qoM6N<$f=&V!qyPW_ diff --git a/DSView/icons/params_dis.png b/DSView/icons/params_dis.png old mode 100644 new mode 100755 index 277a326663dbfe191f2318d57672dab53db1fcc7..af2518a79d3016b5e09083dee660d0d91d47e994 GIT binary patch literal 3502 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 diff --git a/DSView/icons/protocol.png b/DSView/icons/protocol.png old mode 100644 new mode 100755 index 91423ec2215970c2839a8f586d78696ba6d53ce2..acf434e2f3c3dd55e53709aaf3ee9080d72308fc GIT binary patch literal 4180 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 Wpw`;oWFOw=8Gt004R> z004l5008;`004mK004C`008P>0026e000+ooVrmw00006VoOIv0RI600RN!9r;`8x z010qNS#tmY4#WTe4#WYKD-Ig~000McNliru-U$i`A2N&)F?#?211L#EK~!ko?U+AL z6j2n#e|I1dgIUllB*wvDpjsL_f*$~C3-}0BHa50kL+78;X4oELWup|9Xh{JZO$bDw zfy4w*F^Jx4v2WoqEV#47x@_2!ObB=0oVjP-y=U%@m>G?Xpxy~I1=Kr%rhs}UaP0-O zCRr>ZHBTt1ttS#t2}VapC6~)}0D*{LW&#`)3WbA(g@xKEuJot``Fy@xRcBN+a>uNy zK8d2Jj>0Dy|fF}Imnt@@|oU8vy&R9!+tLqkD( zd;1OGRBdFl*|vBLuxvKlHat9hGaf%YJUm!hTB>>^mL$IG*YVog+MT|>zDXbhlt>J? z1w0gy+s9d&Z31tApFSP1zrMcys=vQ~+gqDRK$<4*1}1@?GUr-i|KD|MwVh&E#WgT1MGA9sUHHHX#`X~3yDbCBcIRTSJhAEK~>*JQPg$bHPz;m zY8JY{?pioo+kuF**5V6D!@r6Dn}AwFJ3Nbh@*%bF{j;`p@f=mRceKR}l^GLQPz2sOpreZm4QlEk_U| z95J(9JB{&r3$0jyhY2Zq<^(XOs#En&G^qdpL}VVA2gX!2r>YzAol(_{sxG?NuBwix zYUr=^AArBhA~Nm5VVt|fp{mOlm01!SeGWt-B4+l$%%)T|2fPI^vrZA|0#<-I5&2l! zMG<-Igk^wdz=GqMK93s+-9sz{L2xe!f~|5%RXR$9B9Z~dyqoR;gJu>2Q4j?CE}j8G zGkfQRZMnHWbYCYz;Dxu#y+AK8665Z5Gyh7}CFIIv9C&JG1z=uI(B+ccawV2=m+b>v zA~NU_3`;^v=NxAVdrnxK`$K~2D58uD?W*dCJ6E=@WAU;*;5(Ur4^>@M)n!-nr?|Zy pxUvS)L=#s5&EEs{MpHn&@fVaMs2+~U;zx diff --git a/DSView/icons/protocol_cn.png b/DSView/icons/protocol_cn.png old mode 100644 new mode 100755 index 213fd151b5b2023cc5c565a01d7c31000bd070a3..f1af4fec243d36267a544397932c5ba138de3388 GIT binary patch literal 3269 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 FV1fz_lfeK0 diff --git a/DSView/icons/search-bar.png b/DSView/icons/search-bar.png old mode 100644 new mode 100755 index 20b94274e45735c4f05fc0fb62a11075b533a7d9..1e58096d44e309e6efab825b0200958c308c0f66 GIT binary patch literal 3679 zcmXxnc{CK<{|E3HV;kF8O7>+IA`)4%^kBjmOV+5AH3nm(v2Qb!C0mB9A(|92$d-^v z65>%K>x7}wGub_iJzqV)^ZVm-?>VpgzW3a7?;rQv)XO&J{5%po004j=Wr4Im?l1o- zZqDO2Q)r|1xB~}UxZVK(ct!pc5K#0;>=?Q5DC~gP0Yt#^uw&=>z-PS^+cdtB>w%@6N=sCZo#^XL8@ZHD*;rE;T;m*g4AVWN%6NQ?c7k z05y(@IM~`VY9K>~p@2H{*|RAeEKXS#r_O?8xhlUKMV;8GCu?o>7BsOdZ2abrFwaoX zCwRfJ(wIjm3<&*z>zv3=NN0nBG%>Y9R$U`fJhNup-Cn2wwpxN{U%FZMmMniRd)ss% zhpWErMk31CJ+mWRYhtTrOTsFG4cq!zD)J-zkbL6_*xH@u^cXu2UMxZ_%ZkKr2a&(nL8Ax^$jUwCC7H>d&z|Rc;zu7<t~z zUbNV&^M}(2%s98#5WB$H$VEE@q<4~Y%1BgKM3+Oi!@E_HP(xg`c-?w##%kav%dEse zOpy&MD-gq>TT<=|$(UDQNTDE|C;Ad^%Y9Azdcz5e#AWI&RF~w(;*5R;Qbj^DmA+ExaUEFE{vDN=Fd|GmJrdA;(_{GIcsq%RzCz|N>q1eFB1cY5xWVT6^4RJV+{j;afNFZ4g~M?e>j7=D+4b`ci=1F#fZV8fv^!0n-D5^NzYaUF1mht zZJe|s7iLD-Ck9nKQ2dt{0u3ADXLn5|FV=BTa+-u+W!Be)JGTw>MGuv*^NIR_Hr&g1=9(+X32Bd`5T;%>8Z((TPV&Qwk{kt1h1Hgpub4=?0nAuy6ac z0NQ?@T{svW1&9DpS(;ExD%r_ZlHN%0s>XLo?K~m9m(s@&Qy6-Xf45|6T>gJ%ER^xRF+U3l9>RsF&y{xH6>5Ig)92H*TI|3as?GX06 z`SMgo!!-Y9QmthsTYQzxl?Q!)n_ow?5U@V$XVI?`l$6{6WGR`+bc! zWuFGOLE`{L)ay#8Ns0AM{sWG6=Pg@b)_g=BT zvZT>8XF_{k(jhx-Rk+)11=3M_e*%|>_YKK! zv5XpHogwLt6DYDu*d0~rF{6x+%M6@)@LXwt0n(by&1QexquB5mDSM@uE8dcqpRF+kame;ABO=Kdg|!rhdaNf)pI9-`S~G5FF?1^0Y1 zSJh6c>JvU|kc^xu39Fkc60mt@?jZ3(&!XY&|%&BvC+*I6r zZ?r$_0-6TN5lXtRe@AXk`$Pl41&YyEA^{&X2~GG#A8opZ#d2hiNOGc%ilRIDb-Adl zW~ZV$#X&wtnW(Tw^+E-zn0w2OW~E_zx$0lf-)a8(OI^L&hv$LgSwNio$YV2RnYwQ zGWSi<8Kz{*MU#m!Nx~&6h(u|P4Uv4;Yeg4aJrg6P z8$@Iay}#yp>(g1uBf8_3zs~3%UG;nfu&Z38O1f5 z(THqrX(g7l+}8r_?iruIw}X^xyk|9gM=^uYF=6qgM()|wnavB0$%QY}1dZlkus#Qg zP;issIO{sf5ShS;>z*;>JBapg8D+&G|7mU(~Z0rOQH`YyW^uYC-dn zhY5PYp2E$f-*xDGY$uLRcjNK8dfj^;59L(dORk-dgg$jznicR~J+y+YC&1S37uz zNcKNVlX)+*Ut~G}dy|^td-IZ%){5yX$t)prHc4}suTEZp;H=1GPQmG$cP6|Fa=WhQ z#&iojzDe82zLu3^Yvw0mEmR=*4hIr=rgK(kjF&s?uwtab_@T|7Up*+n59U_~x>oxjBaE zy8FCk*sG2FZwU5vs$;DB_iOwr1A+cUe$KAv@^$x@q5ie`WNk~Y&{Mq^oM>7txF?>V zVc15NYsU;oMdh0Q$P3AwhxeL+#n_u9?S~%qo#}FQWiBgd@ceLdEULUM1+0RomX#Y= zsFn$@Tr?dtQ$9UqbJdsk!$o)d2@lDKZm^d z>lKzM0V&ct`-$ad9VHS^ p=XN;x11MUgTGdmHs5*ZHz*C)ivY#a-9RH&Ls7p3Tiivx|{{U^K$G`vp literal 1520 zcmV004R> z004l5008;`004mK004C`008P>0026e000+ooVrmw00006VoOIv0RI600RN!9r;`8x z010qNS#tmY4#WTe4#WYKD-Ig~000McNliru-U$i`9~f7v_^ki{1yM;vK~!ko?OAP1 zm1P*do|hv>lpz*taCRiNCO`UMp);g9TX_b|GRT!C0YCadYXQytXt<%s^3aDN>qk1* z3OCnAm~H3}I#7${!dhgwlvPVmz|$s@!ttCyeXqM8yy&|l2RVn2gB#cO?ws@NexLWc z?)!f3`+44@LmB0`hhs5-B>)xyAb@KC zu6A^ET=#mtA(5%At;Ocen`cHqAe>HTbVf!-`kp;|UIg$cfCm8R0D1xZeBi)==8}?< zGvUrZX^6xVSi^2uzmG*cI{`a5|j} zo12@ha)#WK0a!u0uXWv)z#Iluv)Dp zN~xtHGUD-gKCiE@uLsZxU}0)%>Qn31tt$fH1hA~Iu&_ke_0H1L(!YH^-}Ea8C55(a z+XkP{_rX}Y=(^tEa5xgo^Vw`Rt+BE3F_I^Zlo}veXtUYw8toZRp;fC^#l*zKI1S;w z-QC?Eoj7sgvf0Mt@i0F>|4e&(`=>Ix(OL>%1pw>xyFc_kpaEDs#`KGRaFZnu)W_z`ak2|$O^c0>C%X<>wg$OAIi$g z%1KH}x?_%AyLK%~Pfy>Vl!_9OVE}&uxIPmCLaIV#Wo3F^Uf$PsyZt$(R1AQ3T3cH$ zxLmFu09-X3V$00TeAQ~TzGU2XuC=xGN59{HBjN%8pt`#HyZ!t3H;c#%0Bp;bFaP+^ zp+nyT=)mZ9;%NXG0PYi!A-CK8<>}L>v3Kv@we9Wg-yA%6aC%cI)J|-%SX6Ow@#9{v zw~3_R=)5;bUL)xvsTSyEnJzG%7xguR(NeE4v$-|zo0 zF)^`m&6+hS>({SO0-&Edb*itfuI{g+N00V;Jf7zPJZO|sD5Y#7vcu(asgaS9Pikvx zr=CvXM!<}Ogao9erDt#elZ0OaQ8MqRmbrGVu501^L?-0pI@V!{;= z_8JHU$BrG-H*MPV)$!xUHH*bk1t3L@B2h$kY}vAfx~_-ip;Cp}vP5HtcxsXhSNBs0z9!6HMl(lmCJWagMH$uq{Mon#!zHnW}CX+g$=BUpn7 zc+f4PBGN#z(jc8_kh2F0GY&Ej8)FTIeVj4gMlvqYDI_aR&uzFHN15owmZoV#nx;Kz z97c|aNDax=BC=X3wOd3)Db=QwvIBSoM#_oE5)nBW;H0z0*Ej%n0B%jwhQ^6{r>1E` zM)aqaLO}=&0q6s8MBPR_fU^MXhTRy+@i2COjyVET!9-Q)Ryru9-UjfBrfHo3Hj2n< zQ>s*In$`#4Go{pSrBu-W1Prfl1YlQ64ThBKK@fB=G~bD53G)IXjrsFHq%kib()b^$ WCHyCCgeG$U0000OUIp z=k>>`N(l<_0~!88hzT-ce=^RO{V7pi;eH^Gu$@14_-{$ zV2~!(37LY7-l(qCJ6V$dOTMTJ9%k1l?vcroj@hq4etb!=PqWy+;Fy;WtJArVs%J@} zw=DeNu9lN`8=s(IoK7c9o^j+nd`y@b6h>Ki>W0PSWilCp>eu1q^YGtJgk8rwnR$W^ z$dN+RsTAR~&P~hgGZFg`#%P-TIC&()bP6kgCWqdZ$K=D9bPHa>NRjE3#s_)iaVT7B zGAXGm`Tb!?V!qGT{Tnc5I_qV`qYFotOOmZNFit{BP$e;!jf*P0h@A_tmdV6}l`{;9 zcs_VzZyd$3EBUV>8YcXhzJfed0?(a;kVL|AfL7FWEuzckDsDewB*OxU291gqcV!14 zn!TmMa)Fw4gB!4)7A17|q}<^bc#lw#ePHV@`|((w7J=zl7SLpM8+ib8qW?p)km@Lewn0XkoUT1U&)2dwvQ!02c^MiGoccw&nP579SypR%h-qY%~E{77RtsQ4l z)O8j8Oeorw(8MN4=Z@)XZ(ZQ63HOnsmP;fVb@bxYkjj4+^}-CpYJt$cdwmVG3$*PZ zphZM!e0F-}AxOC+L|B2o~bebR}tYcLDi z1b78oCSE8WCprhMlFcopEr1eV70ZL-~E|I9kgrxcaoQ#kg`1PS3 zc-8@`F86T|$n3TMMOj7K4mL6$2-PBGgn&~9dqlt9&@s64eRwUtz4Lhrfv?Nc1tF(t zP+doYX0DRJuNC{& zO@Cv9#Y(kP1_Ln5!i&4+&h)+2@s)kY(q;8wCpAghbw*AEWpp8O20gu^KuSaw$>m*93wzmPAj5@4+Y>IHG2W)&=h$?-q(l02jq$-6 zC(blxSzjOXaZ-(NqS^tx^=a!6bBzYH%4yv_IlGc0r+-#e>+3YP*K|*zSe65)D7Bk$ zP0<?=tuy!MmOq+tpOEoH6lbod(>o8buy!Ze&2}efPxZc9^@=9F?4JiVZqQv)E(WUM zy;ILZ0aJ}g@vvgVeHDLBs}vV( z8R;W;Q&XP=Qv(J%;~vqKa$XqC_eeqBqjD`-PsP|w(Nm&i_RXCdWmGVD!`n7ivHDgt z6-8i9{cN~r@;#il^8)f{Mi|$A3U`ZC@Pxyw;$Wq1bSD)6vI;@96f5Bo$l= z=U=gV@r8vYjL{c-K`xPE{`aEzeM!sbb;^gufsLkq{&^k-ge=S8TQ; z=T__Pst(51KAsc7LmP7y)W-gYdKq%GGPvP|{jis^m26Im?_5T+jpEkxzg?;xAGo(& zECgU4r_Z(p#z6g-POGa_Lq#fP{zAr_C!7tt<&eA{=1h3%iM8+WXYWyNRj2}*GCN}m zL*wP;;Vs`1M)SGL*8xeYy35mB`AW>C4lBK@UldB3=#(i4qz_*Rwg5q3g9bZ{v;`u&QiPH*YgD-#q{FY ztzIV%yX@=Pba$zc?y>!!_pFI5%8`x|;?cwu&6#K0oTdmIjUESmR<~dUrYMOY;F!!n`{2K zYEnUXeC^kv*PB`1%BXhb9-RU@Tvoam2FA=e8$Ol<;w61eCXMi%CbgsRU z5a}TUj&~ggxm#_>nW0x?wk3X>(y~fjBIDxRn$@~w#wEzEoHJ#NKm;NF?dM<4oCidG zk|v|p;P{z~ds)X>;>c!4EPyNHWaF^i*hwt1h~B~1>rtKc;O33Zxf*3;UAb4^a^sEo zWl@i|^!2l|9&M&D^3p6T#$!JFXxaU4$5pofj4%kfF6}HiTX)W&(DxKR%G;j5=i@3U3T$8& zb;&Ci@$%Dqcybos3Q|o2cO*E<#H{&rqUn{|UR^GAJt~3h{`&IJBP2d09Ki^|yY zI}x^sF$bpLi;`f!PpGVSpUv=m_;C}+SdB~r-mkXYxmPIFB74nTWRq`9^8Gkk6gO6= zs#_&X+=s~@ee@)WZr9#<=@nzsH*3un>+4h&yVr=?Mf?{+9C4q!@~~i`#OTd(Z9y)k z)@;y?^4BdNnCq9KbGN?4RNq*T(CHXIe{6ppx~J#1w9c)55^H>hd{8CFFT@Z6+3J5J zdjNj$6Mp>59biPZhz1&w*4Srl~Y~DmjK*2_3hW&>Pp_7 zxiX3C&#K7t*CAYP%mHV0rs@2IN0i0ey!*?$ob<}gE4Lu1Z1tSx^x^y6Jos1S9kAn@ z4%t%SRIyrh69x6{8EvtVIqH4#w^4;`$=y)%ZRsh7#ZJc-6vDig>jqP*1@`8eeXUaq zR~hod9dGPSYaHo@+8z;Q7Jvr^re*KOx07FL%P#a7=It??J$83>;%_a5<*|&GjOkLh zI<)h3F(n>y$6RtC*N=rkHebJ~Tq&&Je%8>d;i2p?N38$G569vCg4@p?iy|2}pT>UA z+YoyZOgdlRRCxU0mwqxjdHUsgA+CB+Do&L!3|0hIz| z^IMZ2m?CbsTV`G+-WB<&aQ{_BaIy;TA}jl0B>Yqb!-5?cYUlH4f!U@$rp!e<=t`T2 c=}7NDxSb{dXuq6<;Qs_Vg|o-jSdo(c2Ps#l@c;k- literal 1388 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 diff --git a/DSView/icons/start_dis.png b/DSView/icons/start_dis.png index 62eee6d588acfc5b2f0890b5291e66e7c5aac8ea..56f6e90117665371851f3fe421852cac938698eb 100755 GIT binary patch delta 1988 zcmV;#2Rr!33WE_ziBL{Q4GJ0x0000DNk~Le0000`0000`2nGNE0FDvztpET332;bR za{vGf6951U69E94oEQKA00(qQO+^Rb1q2TjCUf?U(UD2ke+N!UL_t(|ob8-#h*d=x z$A4RvLP%>+trTHGY7tQxxQ5AE8p?td6?z}=rKr#f%n!b-XjzI{e&|bq(P8HEkxPK9fo7m7kl8YD z0{BB!&jqsSMN`a~h=>dXCIORy;lN~|^uzS8*8xL; zcKaDr<&?jHX{x#*kKbGZipXN%CDQMv+kn-;j?iNHVnrPX%mXH*6e$DGtLpMbeKahf zh`bFvf01(NJ79sTZfmqsF%Xdnz{|iLDTm)t)h8RV+t5H-MCOq$-U?vkg(e<=tDFN>S0}|QifXS-5C5x?00*c5m;9JZc zufl0yysCbc#qs$@M8*K0({r4961YoM5Bl24e|ib8rl)uSPlggtM4CiosE7;>VXqdp z&ThXq5EG*}-YwAnVqZGht5mfki^CfB?*ujgHvngV^}t+J{WFC98h%#-3!UtHd>ciU z7Wj&jJ_szyaDG)pp!A~HOLj~Xn&#FE5~wJKf$ipc%IXeWD7!0}xh ze{f|={!JJW)5dT?urVxhvPavP>*Cu2A92#(sOl%4=lU?T#<6DLQxSP4puZm2SiW(x z-I!CUE1+G!d%($F<5#s{TnfAv(R zn};o!*eYCuiM=L10=7HpU9s^G-_ko%|20j8Fb@}$?pxy1X$;pnkv{Z$& zz_Y6Qbi`-OZcLkec6#}a13z}sf1^a?Vb61YI8@`<2~4YtwuEM58RcZVF{k1spsIcb z>~^wO1YDcj3H+6k|1+k)i?)DaV{o4p@3t}5CCf}?zLOpXEX#0yRaEs~;9lStn{@{G z03+Uk5I$NdH`c0TeJ;ESvsp69bX8sFZKSsSCd?AjKdM@H3>LuOe>BVwm1MK3 zx-0NiDITo%<%4x=xP;jTQK3Ju&2E>tD6nD>hi3uML(JiY>myCi7eruQ}wTc+n;&#j9R9 zi7H?k#mj~TJt&?F=cEuYe5Kk@E-7;h>S=n-zzUdor3y^)&fOjAx1EOBXn;;YXw?Vb$_Dl zEMNgJgmAMHw?0vP#Jq&3*uR~?&8m8;M%huAz+w``M@#_+)Gq=ef3gD@;iSR5Xt-BI z+JG6D;BUwLAp3!rRdr9|yqUIKC-4}i+nAd8Ue!nT(7K7)2Udb>0XEd=d)nx^v9Zm5 znr(Up<|b6h8-dvqy<0?D6Xy-FWm++7k_+i>arJyOOiTegFf9dOBCt_>f2nE{=AlrfJ6M_GcL=8MJp!zNh zQSLok1}jpUtpS+U1!kMu2%u9{2ixOItuKI`syfa&zpjr)h&qf>)f1{Z7qeZX1G8oa zFw6E?brF$f8~jmVhy7H#gKGP=KWezr8R?Umh!t`brbDkVf2CUa3bxhlz_uLBCFx#{ zY3#3aJQ#gFmD@^p|CNfp>?=&f+phm=1ias&?AD@Ep2_ zH^~k>Dace{t^J<7bDy$yn=yCLtW@)ni^Lq_Hu<*r8zNG+ z zX>bUNt_X`w&_;uLw9Prr;JDJ2xD z6(}kyGIAW(z;WEWb#-+XkH_HtLakNo;ZMMcHu?d|RFI~U2Kr_(v!+1c4Y zI5-$wSXj_(8fO_G+oP~c*_jj^h#>Wal;`Niw?4u?V^VS0M{g=%pv9G;T$^78Wc z91e%Mf3~*PZZ?}oT`t#9Fc^HMM)W)b9PoNcNy#Vm_4V&mR#w({Jf4yH`6trq>heY+ zk=Ru#rbhq(AOO{NyZuYM-TrAjp7^P|`@6dUqDqDIdKO~HgaD9!ljDpZUA$9mwgChv81b^RC!1fF}nUd{WQMS%UC zf1RDRcszc0Y;5fQ@bK_jEEd~3gFXPX3BUk=F;)QJ@0ppIfj}T|PY{GiI2``7ySrP1 z7RRGHKyh&~0Z46ZY=olG=>7iw{>b9u;%|vWVqXJ)C&JYJH|ln~KWJ!ZaL>-pj`)1O z35|T68r2gpnM_m5%gfU~pD(3_A*HCEe}LZP|Jc$SMQ1=1MGn9OW9*WA>4w#6eIQ>S z1@IZq^OFko6xSW*)B!|MGyw21#vWU(*6NpTUjksrp?|J*Sri>&Ns@d3J|v>+2P^if zsSwfIjIrDD`xFsf2M`A^0bo-$eFK>VIWkS90Ti79BDzIHQO4MWBuOMmQZvufk_8NdL*)$kqQJ&|kd7gj#iaNz8Is-h7`x3FFne->Y_?94q~=@#@Xzng9R* diff --git a/DSView/icons/stop.png b/DSView/icons/stop.png old mode 100644 new mode 100755 index 969a69994e1ef1136389b7947435353a78743317..77aa105d9f535a2f89d6aafb65464da8b78cc45e GIT binary patch literal 2110 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 Vu13Mr0_m9q9C6OrN;}fE{{Y>vzAXR% literal 628 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 literal 840 zcmV-O1GoH%P)004R> z004l5008;`004mK004C`008P>0026e000+ooVrmw00006VoOIv0RI600RN!9r;`8x z010qNS#tmY4#WTe4#WYKD-Ig~000McNliru-U$i`DJm9WI9LDx0;x$vK~!ko?U*rW z6hRb*zkihkLC#>ING?KJ!B&m6SZHB&RyG!PHX?|HaKYZnS}d$g8XFr6v5JLS1k_3; zB*DV!jA$Uy_ggq(JafBu+2fYr{;+VoeK+%eH#2W$PPW<2%BnU{15|CG2B_LV4N$d# z8lY+e|2@FolHb2z`zkP=+FAz|ft!*xb_2j6NoQ<-1v-)@Z66qw`Mv;bUjn9kygn%j zpbb2-{SY`}`!=vS1b<7C9uyfYdS19d5pmh}ctkua${j=z@v6vQKO@jdzj9AW+G~3Z z*kgMg_yGI>RMh@9+5{#g9R@l@$z|8TRtP1C_rQ7J8#}9V>%cAGMnB0z1NhAWY)D!K zVyBJRUh6mazuyPCWnc`L1isnc=@e>=9KgRW*Biib+mHFRhzACcv|+nBGyq8xeVG5A zI{qPPA8@?PkFlld753dv8NDlMy2rZ{lFk7KZBGLGZGQyrODesQNm?pQ>SH6zd#3l*%hKyM6>p z14P73M7)lOzRxcs0f>m@ZLWCj?mS6NNljp?3V@zlbb@F^#DeX)O3W2_v_04qiil=J zET<{691%_1E!&-lm`OP^wmVw}u9K$OKXKcwu90qS*GHbG9jyULZ?h~rnq}E)7hnUp z13XE0c$!)lZJ)}k za)rvPLSe2S_0V1N^U4ioAWdV9#XzH`jh*V!Gt5ywAwSEF6k#uje ST3lKH0000nqmh4 diff --git a/DSView/icons/trigger_dis.png b/DSView/icons/trigger_dis.png old mode 100644 new mode 100755 index 9c804d0eb81aea468c80b574242263ef78debcc4..eb401cff315562160b57869f5f18811b853cac56 GIT binary patch literal 4101 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 literal 820 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 YWiD@WXPfRk8UO$Q07*qoM6N<$f-R5W-~a#s delta 2770 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 + * + * 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 "mathstack.h" -FftStack::FftStack() +#include +#include + +#include +#include +#include +#include + +#define PI 3.1415 + +using namespace boost; +using namespace std; + +namespace pv { +namespace data { + +const QString MathStack::windows_support[5] = { + "Rectangle", + "Hann", + "Hamming", + "Blackman", + "Flat_top" +}; + +const uint64_t MathStack::length_support[5] = { + 1024, + 2048, + 4096, + 8192, + 16384, +}; + +MathStack::MathStack(pv::SigSession &session, int index) : + _session(session), + _index(index), + _dc_ignore(true), + _sample_interval(1), + _math_state(Init) +{ +} + +MathStack::~MathStack() +{ + _xn.clear(); + _xk.clear(); + _power_spectrum.clear(); + fftw_destroy_plan(_fft_plan); +} + +void MathStack::clear() { } +int MathStack::get_index() const +{ + return _index; +} + +uint64_t MathStack::get_sample_num() const +{ + return _sample_num; +} + +void MathStack::set_sample_num(uint64_t num) +{ + _sample_num = num; + _xn.resize(_sample_num); + _xk.resize(_sample_num); + _power_spectrum.resize(_sample_num/2+1); + _fft_plan = fftw_plan_r2r_1d(_sample_num, _xn.data(), _xk.data(), + FFTW_R2HC, FFTW_ESTIMATE); +} + +int MathStack::get_windows_index() const +{ + return _windows_index; +} + +void MathStack::set_windows_index(int index) +{ + _windows_index = index; +} + +bool MathStack::dc_ignored() const +{ + return _dc_ignore; +} + +void MathStack::set_dc_ignore(bool ignore) +{ + _dc_ignore = ignore; +} + +int MathStack::get_sample_interval() const +{ + return _sample_interval; +} + +void MathStack::set_sample_interval(int interval) +{ + _sample_interval = interval; +} + +const std::vector MathStack::get_windows_support() const +{ + std::vector windows; + for (size_t i = 0; i < sizeof(windows_support)/sizeof(windows_support[0]); i++) + { + windows.push_back(windows_support[i]); + } + return windows; +} + +const std::vector MathStack::get_length_support() const +{ + std::vector length; + for (size_t i = 0; i < sizeof(length_support)/sizeof(length_support[0]); i++) + { + length.push_back(length_support[i]); + } + return length; +} + +const std::vector MathStack::get_fft_spectrum() const +{ + std::vector empty; + if (_math_state == Stopped) + return _power_spectrum; + else + return empty; +} + +const double MathStack::get_fft_spectrum(uint64_t index) const +{ + if (_math_state == Stopped && index < _power_spectrum.size()) + return _power_spectrum[index]; + else + return -1; +} + +void MathStack::calc_fft() +{ + _math_state = Running; + // Get the dso data + boost::shared_ptr data; + boost::shared_ptr dsoSig; + BOOST_FOREACH(const boost::shared_ptr s, _session.get_signals()) { + if (dsoSig = dynamic_pointer_cast(s)) { + if (dsoSig->get_index() == _index && dsoSig->enabled()) { + data = dsoSig->dso_data(); + break; + } + } + } + + if (!data) + return; + + // Check we have a snapshot of data + const deque< boost::shared_ptr > &snapshots = + data->get_snapshots(); + if (snapshots.empty()) + return; + _snapshot = snapshots.front(); + + if (_snapshot->get_sample_count() < _sample_num*_sample_interval) + return; + + // Get the samplerate and start time + _start_time = data->get_start_time(); + _samplerate = data->samplerate(); + if (_samplerate == 0.0) + _samplerate = 1.0; + + // prepare _xn data + const double offset = dsoSig->get_zeroValue(); + const double vscale = dsoSig->get_vDialValue() * dsoSig->get_factor() * DS_CONF_DSO_VDIVS / (1000*255.0); + const uint16_t step = _snapshot->get_channel_num() * _sample_interval; + const uint8_t *const samples = _snapshot->get_samples(0, _sample_num*_sample_interval-1, _index); + double wsum = 0; + for (int i = 0; i < _sample_num; i++) { + double w = window(i, _windows_index); + _xn[i] = ((double)samples[i*step] - offset) * vscale * w; + wsum += w; + } + + // fft + fftw_execute(_fft_plan); + + // calculate power spectrum + _power_spectrum[0] = abs(_xk[0])/wsum; /* DC component */ + for (int k = 1; k < (_sample_num + 1) / 2; ++k) /* (k < N/2 rounded up) */ + _power_spectrum[k] = sqrt((_xk[k]*_xk[k] + _xk[_sample_num-k]*_xk[_sample_num-k]) * 2) / wsum; + if (_sample_num % 2 == 0) /* N is even */ + _power_spectrum[_sample_num/2] = abs(_xk[_sample_num/2])/wsum; /* Nyquist freq. */ + + _math_state = Stopped; +} + +double MathStack::window(uint64_t i, int type) +{ + const double n_m_1 = _sample_num-1; + switch(type) { + case 1: // Hann window + return 0.5*(1-cos(2*PI*i/n_m_1)); + case 2: // Hamming window + return 0.54-0.46*cos(2*PI*i/n_m_1); + case 3: // Blackman window + return 0.42659-0.49656*cos(2*PI*i/n_m_1) + 0.076849*cos(4*PI*i/n_m_1); + case 4: // Flat_top window + return 1-1.93*cos(2*PI*i/n_m_1)+1.29*cos(4*PI*i/n_m_1)- + 0.388*cos(6*PI*i/n_m_1)+0.028*cos(8*PI*i/n_m_1); + default: + return 1; + } +} + +} // namespace data +} // namespace pv diff --git a/DSView/pv/data/mathstack.h b/DSView/pv/data/mathstack.h index c1449463..dbbe21bc 100644 --- a/DSView/pv/data/mathstack.h +++ b/DSView/pv/data/mathstack.h @@ -1,11 +1,119 @@ -#ifndef FFTSTACK_H -#define FFTSTACK_H +/* + * This file is part of the PulseView project. + * + * Copyright (C) 2012 Joel Holdsworth + * + * 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 + */ +#ifndef DSVIEW_PV_DATA_MATHSTACK_H +#define DSVIEW_PV_DATA_MATHSTACK_H -class FftStack +#include "signaldata.h" + +#include + +#include +#include +#include + +#include + +#include +#include + +namespace pv { + +class SigSession; + +namespace view { +class DsoSignal; +} + +namespace data { + +class DsoSnapshot; +class Dso; + +class MathStack : public QObject, public SignalData { + Q_OBJECT + +private: + static const QString windows_support[5]; + static const uint64_t length_support[5]; + public: - FftStack(); + enum math_state { + Init, + Stopped, + Running + }; + +public: + MathStack(pv::SigSession &_session, int index); + virtual ~MathStack(); + void clear(); + + int get_index() const; + + uint64_t get_sample_num() const; + void set_sample_num(uint64_t num); + + int get_windows_index() const; + void set_windows_index(int index); + + const std::vector get_windows_support() const; + const std::vector get_length_support() const; + + bool dc_ignored() const; + void set_dc_ignore(bool ignore); + + int get_sample_interval() const; + void set_sample_interval(int interval); + + const std::vector get_fft_spectrum() const; + const double get_fft_spectrum(uint64_t index) const; + + void calc_fft(); + + double window(uint64_t i, int type); + +signals: + +private: + pv::SigSession &_session; + + int _index; + uint64_t _sample_num; + int _windows_index; + bool _dc_ignore; + int _sample_interval; + + boost::shared_ptr _snapshot; + + std::unique_ptr _math_thread; + math_state _math_state; + + fftw_plan _fft_plan; + std::vector _xn; + std::vector _xk; + std::vector _power_spectrum; }; -#endif // FFTSTACK_H +} // namespace data +} // namespace pv + +#endif // DSVIEW_PV_DATA_MATHSTACK_H diff --git a/DSView/pv/dialogs/fftoptions.cpp b/DSView/pv/dialogs/fftoptions.cpp index 576975c3..b73ac46e 100644 --- a/DSView/pv/dialogs/fftoptions.cpp +++ b/DSView/pv/dialogs/fftoptions.cpp @@ -1,7 +1,257 @@ +/* + * This file is part of the DSView project. + * DSView is based on PulseView. + * + * Copyright (C) 2012 Joel Holdsworth + * Copyright (C) 2013 DreamSourceLab + * + * 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 "fftoptions.h" -FftOptions::FftOptions() -{ +#include +#include +#include +#include + +#include "../sigsession.h" +#include "../data/mathstack.h" +#include "../view/trace.h" +#include "../view/dsosignal.h" +#include "../view/mathtrace.h" + +using namespace boost; +using namespace std; + +namespace pv { +namespace dialogs { + +FftOptions::FftOptions(QWidget *parent, SigSession &session) : + QDialog(parent), + _session(session), + _button_box(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, + Qt::Horizontal, this) +{ + _en_checkbox = new QCheckBox(this); + _len_combobox = new QComboBox(this); + _interval_combobox = new QComboBox(this); + _ch_combobox = new QComboBox(this); + _window_combobox = new QComboBox(this); + _dc_checkbox = new QCheckBox(this); + _dc_checkbox->setChecked(true); + _view_combobox = new QComboBox(this); + _dbv_combobox = new QComboBox(this); + + // setup _ch_combobox + BOOST_FOREACH(const boost::shared_ptr s, _session.get_signals()) { + boost::shared_ptr dsoSig; + if (dsoSig = dynamic_pointer_cast(s)) { + _ch_combobox->addItem(dsoSig->get_name(), qVariantFromValue(dsoSig->get_index())); + } + } + + // setup _window_combobox _len_combobox + _sample_limit = 0; + GVariant* gvar = _session.get_device()->get_config(NULL, NULL, SR_CONF_MAX_DSO_SAMPLELIMITS); + if (gvar != NULL) { + _sample_limit = g_variant_get_uint64(gvar) * 0.5; + g_variant_unref(gvar); + } else { + qDebug() << "ERROR: config_get SR_CONF_MAX_DSO_SAMPLELIMITS failed."; + } + std::vector windows; + std::vector length; + std::vector view_modes; + std::vector dbv_ranges; + BOOST_FOREACH(const boost::shared_ptr t, _session.get_math_signals()) { + boost::shared_ptr mathTrace; + if (mathTrace = dynamic_pointer_cast(t)) { + windows = mathTrace->get_math_stack()->get_windows_support(); + length = mathTrace->get_math_stack()->get_length_support(); + view_modes = mathTrace->get_view_modes_support(); + dbv_ranges = mathTrace->get_dbv_ranges(); + break; + } + } + assert(windows.size() > 0); + assert(length.size() > 0); + assert(view_modes.size() > 0); + assert(dbv_ranges.size() > 0); + for (int i = 0; i < windows.size(); i++) + { + _window_combobox->addItem(windows[i], + qVariantFromValue(i)); + } + for (int i = 0; i < length.size(); i++) + { + if (length[i] < _sample_limit) + _len_combobox->addItem(QString::number(length[i]), + qVariantFromValue(length[i])); + else + break; + } + assert(_len_combobox->count() > 0); + _len_combobox->setCurrentIndex(_len_combobox->count()-1); + + const int max_interval = _sample_limit/_len_combobox->currentData().toLongLong(); + for (int i = 1; i <= max_interval; i*=2) + { + _interval_combobox->addItem(QString::number(i), + qVariantFromValue(i)); + } + for (int i = 0; i < view_modes.size(); i++) + { + _view_combobox->addItem(view_modes[i], + qVariantFromValue(i)); + } + for (int i = 0; i < dbv_ranges.size(); i++) + { + _dbv_combobox->addItem(QString::number(dbv_ranges[i]), + qVariantFromValue(dbv_ranges[i])); + } + + // load current settings + BOOST_FOREACH(const boost::shared_ptr t, _session.get_math_signals()) { + boost::shared_ptr mathTrace; + if (mathTrace = dynamic_pointer_cast(t)) { + if (mathTrace->enabled()) { + _en_checkbox->setChecked(true); + for (int i = 0; i < _ch_combobox->count(); i++) { + if (mathTrace->get_index() == _ch_combobox->itemData(i).toInt()) { + _ch_combobox->setCurrentIndex(i); + break; + } + } + for (int i = 0; i < _len_combobox->count(); i++) { + if (mathTrace->get_math_stack()->get_sample_num() == _len_combobox->itemData(i).toLongLong()) { + _len_combobox->setCurrentIndex(i); + break; + } + } + _interval_combobox->clear(); + const int max_interval = _sample_limit/_len_combobox->currentData().toLongLong(); + for (int i = 1; i <= max_interval; i*=2) + { + _interval_combobox->addItem(QString::number(i), + qVariantFromValue(i)); + } + for (int i = 0; i < _interval_combobox->count(); i++) { + if (mathTrace->get_math_stack()->get_sample_interval() == _interval_combobox->itemData(i).toInt()) { + _interval_combobox->setCurrentIndex(i); + break; + } + } + for (int i = 0; i < _dbv_combobox->count(); i++) { + if (mathTrace->dbv_range() == _dbv_combobox->itemData(i).toLongLong()) { + _dbv_combobox->setCurrentIndex(i); + break; + } + } + _window_combobox->setCurrentIndex(mathTrace->get_math_stack()->get_windows_index()); + _dc_checkbox->setChecked(mathTrace->get_math_stack()->dc_ignored()); + _view_combobox->setCurrentIndex(mathTrace->view_mode()); + } + } + } + + _flayout = new QFormLayout(); + _flayout->addRow(new QLabel(tr("FFT Enable: "), this), _en_checkbox); + _flayout->addRow(new QLabel(tr("FFT Length: "), this), _len_combobox); + _flayout->addRow(new QLabel(tr("Sample Interval: "), this), _interval_combobox); + _flayout->addRow(new QLabel(tr("FFT Source: "), this), _ch_combobox); + _flayout->addRow(new QLabel(tr("FFT Window: "), this), _window_combobox); + _flayout->addRow(new QLabel(tr("DC Ignored: "), this), _dc_checkbox); + _flayout->addRow(new QLabel(tr("Y-axis Mode: "), this), _view_combobox); + _flayout->addRow(new QLabel(tr("DBV Range: "), this), _dbv_combobox); + + _hlayout = new QHBoxLayout(); + _hlayout->addLayout(_flayout); + _hint_label = new QLabel(this); + QString hint_pic= ":/icons/" + _window_combobox->currentText()+".png"; + QPixmap pixmap(hint_pic); + _hint_label->setPixmap(pixmap); + _hlayout->addWidget(_hint_label); + + _layout = new QVBoxLayout(this); + _layout->addLayout(_hlayout); + _layout->addWidget(&_button_box); + setLayout(_layout); + + connect(&_button_box, SIGNAL(accepted()), this, SLOT(accept())); + connect(&_button_box, SIGNAL(rejected()), this, SLOT(reject())); + connect(_window_combobox, SIGNAL(currentIndexChanged(QString)), this, SLOT(window_changed(QString))); + connect(_len_combobox, SIGNAL(currentIndexChanged(int)), this, SLOT(len_changed(int))); } +void FftOptions::accept() +{ + using namespace Qt; + + QDialog::accept(); + + BOOST_FOREACH(const boost::shared_ptr t, _session.get_math_signals()) { + boost::shared_ptr mathTrace; + if (mathTrace = dynamic_pointer_cast(t)) { + mathTrace->set_enable(false); + if (mathTrace->get_index() == _ch_combobox->currentData().toInt()) { + mathTrace->get_math_stack()->set_dc_ignore(_dc_checkbox->isChecked()); + mathTrace->get_math_stack()->set_sample_num(_len_combobox->currentData().toULongLong()); + mathTrace->get_math_stack()->set_sample_interval(_interval_combobox->currentData().toInt()); + mathTrace->get_math_stack()->set_windows_index(_window_combobox->currentData().toInt()); + mathTrace->set_view_mode(_view_combobox->currentData().toInt()); + //mathTrace->init_zoom(); + mathTrace->set_dbv_range(_dbv_combobox->currentData().toInt()); + mathTrace->set_enable(_en_checkbox->isChecked()); + if (_session.get_capture_state() == SigSession::Stopped && + mathTrace->enabled()) + mathTrace->get_math_stack()->calc_fft(); + } + } + } + _session.mathTraces_rebuild(); +} + +void FftOptions::reject() +{ + using namespace Qt; + + QDialog::reject(); +} + +void FftOptions::window_changed(QString str) +{ + QString hint_pic= ":/icons/" + str +".png"; + QPixmap pixmap(hint_pic); + _hint_label->setPixmap(pixmap); +} + +void FftOptions::len_changed(int index) +{ + int pre_index = _interval_combobox->currentIndex(); + _interval_combobox->clear(); + const int max_interval = _sample_limit/_len_combobox->itemData(index).toLongLong(); + for (int i = 1; i <= max_interval; i*=2) + { + _interval_combobox->addItem(QString::number(i), + qVariantFromValue(i)); + } + _interval_combobox->setCurrentIndex(pre_index); +} + +} // namespace dialogs +} // namespace pv diff --git a/DSView/pv/dialogs/fftoptions.h b/DSView/pv/dialogs/fftoptions.h index 618c0618..534ef3e0 100644 --- a/DSView/pv/dialogs/fftoptions.h +++ b/DSView/pv/dialogs/fftoptions.h @@ -1,11 +1,87 @@ -#ifndef FFTOPTIONS_H -#define FFTOPTIONS_H +/* + * This file is part of the DSView project. + * DSView is based on PulseView. + * + * Copyright (C) 2012 Joel Holdsworth + * Copyright (C) 2013 DreamSourceLab + * + * 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 + */ -class FftOptions +#ifndef DSVIEW_PV_FFTOPTIONS_H +#define DSVIEW_PV_FFTOPTIONS_H + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "../device/devinst.h" + +namespace pv { + +class SigSession; + +namespace dialogs { + +class FftOptions : public QDialog { + Q_OBJECT + +private: + + public: - FftOptions(); + FftOptions(QWidget *parent, SigSession &session); + +protected: + void accept(); + void reject(); + +private slots: + void window_changed(QString str); + void len_changed(int index); + +private: + SigSession &_session; + uint64_t _sample_limit; + + QComboBox *_len_combobox; + QComboBox *_interval_combobox; + QCheckBox *_en_checkbox; + QComboBox *_ch_combobox; + QComboBox *_window_combobox; + QCheckBox *_dc_checkbox; + QComboBox *_view_combobox; + QComboBox *_dbv_combobox; + + QLabel *_hint_label; + QFormLayout *_flayout; + QHBoxLayout *_hlayout; + QVBoxLayout *_layout; + QDialogButtonBox _button_box; + }; -#endif // FFTOPTIONS_H +} // namespace dialogs +} // namespace pv + +#endif // DSVIEW_PV_FFTOPTIONS_H diff --git a/DSView/pv/dock/measuredock.cpp b/DSView/pv/dock/measuredock.cpp index 6a329021..c49f0743 100644 --- a/DSView/pv/dock/measuredock.cpp +++ b/DSView/pv/dock/measuredock.cpp @@ -134,7 +134,7 @@ MeasureDock::MeasureDock(QWidget *parent, View &view, SigSession &session) : connect(_t3_comboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(delta_update())); connect(_fen_checkBox, SIGNAL(stateChanged(int)), &_view, SLOT(set_measure_en(int))); - connect(_view.get_viewport(), SIGNAL(measure_updated()), this, SLOT(measure_updated())); + connect(&_view, SIGNAL(measure_updated()), this, SLOT(measure_updated())); this->setWidget(_widget); _widget->setGeometry(0, 0, sizeHint().width(), 2000); @@ -222,10 +222,10 @@ void MeasureDock::cursor_update() void MeasureDock::measure_updated() { - _width_label->setText(_view.get_viewport()->get_measure("width")); - _period_label->setText(_view.get_viewport()->get_measure("period")); - _freq_label->setText(_view.get_viewport()->get_measure("frequency")); - _duty_label->setText(_view.get_viewport()->get_measure("duty")); + _width_label->setText(_view.get_measure("width")); + _period_label->setText(_view.get_measure("period")); + _freq_label->setText(_view.get_measure("frequency")); + _duty_label->setText(_view.get_measure("duty")); } void MeasureDock::cursor_moved() diff --git a/DSView/pv/mainwindow.cpp b/DSView/pv/mainwindow.cpp index 90417108..0806142a 100644 --- a/DSView/pv/mainwindow.cpp +++ b/DSView/pv/mainwindow.cpp @@ -112,6 +112,7 @@ void MainWindow::setup_ui() { setObjectName(QString::fromUtf8("MainWindow")); setMinimumHeight(680); + setMinimumWidth(300); resize(1024, 768); // Set the window icon @@ -129,7 +130,7 @@ void MainWindow::setup_ui() // Setup the sampling bar _sampling_bar = new toolbars::SamplingBar(_session, this); - _trig_bar = new toolbars::TrigBar(this); + _trig_bar = new toolbars::TrigBar(_session, this); _file_bar = new toolbars::FileBar(_session, this); _logo_bar = new toolbars::LogoBar(_session, this); @@ -295,18 +296,8 @@ void MainWindow::update_device_list() #ifdef ENABLE_DECODE _protocol_widget->del_all_protocol(); #endif - _trig_bar->close_all(); - - if (_session.get_device()->dev_inst()->mode == LOGIC) { - _trig_bar->enable_protocol(true); - } else { - _trig_bar->enable_protocol(false); - } - if (_session.get_device()->dev_inst()->mode == DSO) { - _sampling_bar->enable_toggle(false); - } else { - _sampling_bar->enable_toggle(true); - } + _trig_bar->reload(); + _sampling_bar->reload(); shared_ptr selected_device = _session.get_device(); _device_manager.add_device(selected_device); @@ -534,8 +525,7 @@ void MainWindow::closeEvent(QCloseEvent *event) void MainWindow::on_protocol(bool visible) { #ifdef ENABLE_DECODE - if (_session.get_device()->dev_inst()->mode == LOGIC) - _protocol_dock->setVisible(visible); + _protocol_dock->setVisible(visible); #endif } diff --git a/DSView/pv/sigsession.cpp b/DSView/pv/sigsession.cpp index 8b0802cf..e0b443df 100644 --- a/DSView/pv/sigsession.cpp +++ b/DSView/pv/sigsession.cpp @@ -42,12 +42,14 @@ #include "data/decoderstack.h" #include "data/decode/decoder.h" #include "data/decodermodel.h" +#include "data/mathstack.h" #include "view/analogsignal.h" #include "view/dsosignal.h" #include "view/logicsignal.h" #include "view/groupsignal.h" #include "view/decodetrace.h" +#include "view/mathtrace.h" #include #include @@ -496,7 +498,6 @@ set< boost::shared_ptr > SigSession::get_data() const assert(sig); data.insert(sig->data()); } - data.insert(_group_data); return data; } @@ -605,6 +606,12 @@ void SigSession::read_sample_rate(const sr_dev_inst *const sdi) assert(data); data->set_samplerate(sample_rate); } + BOOST_FOREACH(const boost::shared_ptr m, _math_traces) + { + assert(m); + m->get_math_stack()->set_samplerate(sample_rate); + } + _group_data->set_samplerate(sample_rate); } void SigSession::feed_in_header(const sr_dev_inst *sdi) @@ -791,10 +798,10 @@ void SigSession::init_signals() _signals.clear(); vector< boost::shared_ptr >().swap(_signals); _signals = sigs; - - signals_changed(); - data_updated(); } + + mathTraces_rebuild(); + data_updated(); } void SigSession::reload() @@ -854,7 +861,7 @@ void SigSession::reload() _signals = sigs; } - signals_changed(); + mathTraces_rebuild(); } void SigSession::refresh(int holdtime) @@ -1009,6 +1016,14 @@ void SigSession::feed_in_dso(const sr_datafeed_dso &dso) return; } + // reset scale of dso signal + BOOST_FOREACH(const boost::shared_ptr m, _math_traces) + { + assert(m); + if (m->enabled()) + m->get_math_stack()->calc_fft(); + } + receive_data(dso.num_samples); data_updated(); //if (!_instant) @@ -1401,7 +1416,41 @@ pv::data::DecoderModel* SigSession::get_decoder_model() const { return _decoder_model; } - #endif +void SigSession::mathTraces_rebuild() +{ + bool has_dso_signal = false; + BOOST_FOREACH(const boost::shared_ptr s, _signals) { + boost::shared_ptr dsoSig; + if (dsoSig = dynamic_pointer_cast(s)) { + has_dso_signal = true; + // check already have + std::vector< boost::shared_ptr >::iterator iter = _math_traces.begin(); + for(int i = 0; i < _math_traces.size(); i++, iter++) + if ((*iter)->get_index() == dsoSig->get_index()) + break; + // if not, rebuild + if (iter == _math_traces.end()) { + boost::shared_ptr math_stack( + new data::MathStack(*this, dsoSig->get_index())); + boost::shared_ptr math_trace( + new view::MathTrace(*this, math_stack, dsoSig->get_index())); + _math_traces.push_back(math_trace); + } + } + } + + if (!has_dso_signal) + _math_traces.clear(); + + signals_changed(); +} + +vector< boost::shared_ptr > SigSession::get_math_signals() +{ + lock_guard lock(_signals_mutex); + return _math_traces; +} + } // namespace pv diff --git a/DSView/pv/sigsession.h b/DSView/pv/sigsession.h index de159f17..9005ab7f 100644 --- a/DSView/pv/sigsession.h +++ b/DSView/pv/sigsession.h @@ -78,6 +78,7 @@ namespace view { class Signal; class GroupSignal; class DecodeTrace; +class MathTrace; } namespace decoder { @@ -157,6 +158,9 @@ public: pv::data::DecoderModel* get_decoder_model() const; + std::vector< boost::shared_ptr > + get_math_signals(); + #endif void init_signals(); @@ -178,6 +182,8 @@ public: bool get_data_lock(); + void mathTraces_rebuild(); + private: void set_capture_state(capture_state state); @@ -239,6 +245,7 @@ private: std::vector< boost::shared_ptr > _decode_traces; pv::data::DecoderModel *_decoder_model; #endif + std::vector< boost::shared_ptr > _math_traces; mutable boost::mutex _data_mutex; boost::shared_ptr _logic_data; diff --git a/DSView/pv/toolbars/filebar.cpp b/DSView/pv/toolbars/filebar.cpp index 5562db7a..cd7e0d94 100644 --- a/DSView/pv/toolbars/filebar.cpp +++ b/DSView/pv/toolbars/filebar.cpp @@ -96,7 +96,7 @@ FileBar::FileBar(SigSession &session, QWidget *parent) : _action_export = new QAction(this); _action_export->setText(QApplication::translate("File", "&Export...", 0)); - _action_export->setIcon(QIcon::fromTheme("file",QIcon(":/icons/instant.png"))); + _action_export->setIcon(QIcon::fromTheme("file",QIcon(":/icons/export.png"))); _action_export->setObjectName(QString::fromUtf8("actionExport")); connect(_action_export, SIGNAL(triggered()), this, SLOT(on_actionExport_triggered())); diff --git a/DSView/pv/toolbars/samplingbar.cpp b/DSView/pv/toolbars/samplingbar.cpp index ed758736..466e382d 100644 --- a/DSView/pv/toolbars/samplingbar.cpp +++ b/DSView/pv/toolbars/samplingbar.cpp @@ -154,8 +154,8 @@ SamplingBar::SamplingBar(SigSession &session, QWidget *parent) : addWidget(&_sample_count); addWidget(new QLabel(tr(" @ "))); addWidget(&_sample_rate); - addWidget(&_run_stop_button); - addWidget(&_instant_button); + _run_stop_action = addWidget(&_run_stop_button); + _instant_action = addWidget(&_instant_button); } void SamplingBar::set_device_list( @@ -785,5 +785,29 @@ void SamplingBar::show_session_error( msg.exec(); } +void SamplingBar::reload() +{ + if (_session.get_device()->dev_inst()->mode == LOGIC) { + _icon_instant = QIcon(":/icons/instant.png"); + _icon_instant_dis = QIcon(":/icons/instant_dis.png"); + _instant_button.setIcon(_icon_instant); + _run_stop_action->setVisible(true); + _instant_action->setVisible(true); + enable_toggle(true); + } else if (_session.get_device()->dev_inst()->mode == ANALOG) { + _run_stop_action->setVisible(true); + _instant_action->setVisible(false); + enable_toggle(true); + } else if (_session.get_device()->dev_inst()->mode == DSO) { + _icon_instant = QIcon(":/icons/single.png"); + _icon_instant_dis = QIcon(":/icons/single_dis.png"); + _instant_button.setIcon(_icon_instant); + _run_stop_action->setVisible(true); + _instant_action->setVisible(true); + enable_toggle(false); + } + update(); +} + } // namespace toolbars } // namespace pv diff --git a/DSView/pv/toolbars/samplingbar.h b/DSView/pv/toolbars/samplingbar.h index 4055a1af..ad48c179 100644 --- a/DSView/pv/toolbars/samplingbar.h +++ b/DSView/pv/toolbars/samplingbar.h @@ -120,6 +120,7 @@ private slots: public slots: void on_configure(); void zero_adj(); + void reload(); private: SigSession &_session; @@ -145,6 +146,8 @@ private: QIcon _icon_instant_dis; QToolButton _run_stop_button; QToolButton _instant_button; + QAction* _run_stop_action; + QAction* _instant_action; bool _instant; }; diff --git a/DSView/pv/toolbars/trigbar.cpp b/DSView/pv/toolbars/trigbar.cpp index 143f9a30..bbd580a2 100644 --- a/DSView/pv/toolbars/trigbar.cpp +++ b/DSView/pv/toolbars/trigbar.cpp @@ -22,19 +22,27 @@ #include "trigbar.h" +#include "../sigsession.h" +#include "../device/devinst.h" +#include "../dialogs/fftoptions.h" + +#include namespace pv { namespace toolbars { -TrigBar::TrigBar(QWidget *parent) : +TrigBar::TrigBar(SigSession &session, QWidget *parent) : QToolBar("Trig Bar", parent), + _session(session), _enable(true), _trig_button(this), _protocol_button(this), _measure_button(this), - _search_button(this) + _search_button(this), + _math_button(this) { setMovable(false); + setContentsMargins(0,0,0,0); connect(&_trig_button, SIGNAL(clicked()), this, SLOT(trigger_clicked())); @@ -60,6 +68,8 @@ TrigBar::TrigBar(QWidget *parent) : _search_button.setIcon(QIcon::fromTheme("trig", QIcon(":/icons/search-bar_cn.png"))); _search_button.setCheckable(true); + _math_button.setIcon(QIcon::fromTheme("trig", + QIcon(":/icons/math_cn.png"))); #else _trig_button.setIcon(QIcon::fromTheme("trig", QIcon(":/icons/trigger.png"))); @@ -75,12 +85,29 @@ TrigBar::TrigBar(QWidget *parent) : _search_button.setIcon(QIcon::fromTheme("trig", QIcon(":/icons/search-bar.png"))); _search_button.setCheckable(true); + _math_button.setIcon(QIcon::fromTheme("trig", + QIcon(":/icons/math.png"))); #endif - addWidget(&_trig_button); - addWidget(&_protocol_button); - addWidget(&_measure_button); - addWidget(&_search_button); + _action_fft = new QAction(this); + _action_fft->setText(QApplication::translate( + "Math", "&FFT", 0)); + _action_fft->setIcon(QIcon::fromTheme("Math", + QIcon(":/icons/fft.png"))); + _action_fft->setObjectName(QString::fromUtf8("actionFft")); + connect(_action_fft, SIGNAL(triggered()), this, SLOT(on_actionFft_triggered())); + + _math_menu = new QMenu(this); + _math_menu->setContentsMargins(0,0,0,0); + _math_menu->addAction(_action_fft); + _math_button.setPopupMode(QToolButton::InstantPopup); + _math_button.setMenu(_math_menu); + + _trig_action = addWidget(&_trig_button); + _protocol_action = addWidget(&_protocol_button); + _measure_action = addWidget(&_measure_button); + _search_action = addWidget(&_search_button); + _math_action = addWidget(&_math_button); } void TrigBar::protocol_clicked() @@ -109,6 +136,7 @@ void TrigBar::enable_toggle(bool enable) _protocol_button.setDisabled(!enable); _measure_button.setDisabled(!enable); _search_button.setDisabled(!enable); + _math_button.setDisabled(!enable); #ifdef LANGUAGE_ZH_CN _trig_button.setIcon(enable ? QIcon::fromTheme("trig", QIcon(":/icons/trigger_cn.png")) : @@ -119,6 +147,8 @@ void TrigBar::enable_toggle(bool enable) QIcon::fromTheme("trig", QIcon(":/icons/measure_dis_cn.png"))); _search_button.setIcon(enable ? QIcon::fromTheme("trig", QIcon(":/icons/search-bar_cn.png")) : QIcon::fromTheme("trig", QIcon(":/icons/search-bar_dis_cn.png"))); + _math_button.setIcon(enable ? QIcon::fromTheme("trig", QIcon(":/icons/math_cn.png")) : + QIcon::fromTheme("trig", QIcon(":/icons/math_dis_cn.png"))); #else _trig_button.setIcon(enable ? QIcon::fromTheme("trig", QIcon(":/icons/trigger.png")) : QIcon::fromTheme("trig", QIcon(":/icons/trigger_dis.png"))); @@ -128,6 +158,9 @@ void TrigBar::enable_toggle(bool enable) QIcon::fromTheme("trig", QIcon(":/icons/measure_dis.png"))); _search_button.setIcon(enable ? QIcon::fromTheme("trig", QIcon(":/icons/search-bar.png")) : QIcon::fromTheme("trig", QIcon(":/icons/search-bar_dis.png"))); + _math_button.setIcon(enable ? QIcon::fromTheme("trig", QIcon(":/icons/math.png")) : + QIcon::fromTheme("trig", QIcon(":/icons/math_dis.png"))); + #endif } @@ -163,5 +196,36 @@ void TrigBar::close_all() } } +void TrigBar::reload() +{ + close_all(); + if (_session.get_device()->dev_inst()->mode == LOGIC) { + _trig_action->setVisible(true); + _protocol_action->setVisible(true); + _measure_action->setVisible(true); + _search_action->setVisible(true); + _math_action->setVisible(false); + } else if (_session.get_device()->dev_inst()->mode == ANALOG) { + _trig_action->setVisible(false); + _protocol_action->setVisible(false); + _measure_action->setVisible(true); + _search_action->setVisible(false); + _math_action->setVisible(false); + } else if (_session.get_device()->dev_inst()->mode == DSO) { + _trig_action->setVisible(true); + _protocol_action->setVisible(false); + _measure_action->setVisible(true); + _search_action->setVisible(false); + _math_action->setVisible(true); + } + update(); +} + +void TrigBar::on_actionFft_triggered() +{ + pv::dialogs::FftOptions fft_dlg(this, _session); + fft_dlg.exec(); +} + } // namespace toolbars } // namespace pv diff --git a/DSView/pv/toolbars/trigbar.h b/DSView/pv/toolbars/trigbar.h index 81e72ef2..7100799e 100644 --- a/DSView/pv/toolbars/trigbar.h +++ b/DSView/pv/toolbars/trigbar.h @@ -26,19 +26,25 @@ #include #include +#include +#include namespace pv { + +class SigSession; + namespace toolbars { class TrigBar : public QToolBar { Q_OBJECT public: - explicit TrigBar(QWidget *parent = 0); + explicit TrigBar(SigSession &session, QWidget *parent = 0); void enable_toggle(bool enable); void enable_protocol(bool enable); void close_all(); + void reload(); signals: void on_protocol(bool visible); @@ -52,12 +58,24 @@ public slots: void measure_clicked(); void search_clicked(); + void on_actionFft_triggered(); + private: + SigSession& _session; bool _enable; QToolButton _trig_button; QToolButton _protocol_button; QToolButton _measure_button; QToolButton _search_button; + QToolButton _math_button; + QAction* _trig_action; + QAction* _protocol_action; + QAction* _measure_action; + QAction* _search_action; + QAction* _math_action; + + QMenu* _math_menu; + QAction* _action_fft; }; diff --git a/DSView/pv/view/analogsignal.cpp b/DSView/pv/view/analogsignal.cpp index 595dff32..de501262 100644 --- a/DSView/pv/view/analogsignal.cpp +++ b/DSView/pv/view/analogsignal.cpp @@ -54,7 +54,7 @@ const float AnalogSignal::EnvelopeThreshold = 256.0f; AnalogSignal::AnalogSignal(boost::shared_ptr dev_inst, boost::shared_ptr data, const sr_channel * const probe) : - Signal(dev_inst, probe, SR_CHANNEL_ANALOG), + Signal(dev_inst, probe), _data(data) { _colour = SignalColours[probe->index % countof(SignalColours)]; diff --git a/DSView/pv/view/decodetrace.cpp b/DSView/pv/view/decodetrace.cpp index bd076af5..b7117769 100644 --- a/DSView/pv/view/decodetrace.cpp +++ b/DSView/pv/view/decodetrace.cpp @@ -836,7 +836,7 @@ void DecodeTrace::on_new_decode_data() void DecodeTrace::on_decode_done() { if (_view) { - _view->set_need_update(true); + _view->set_update(_viewport, true); _view->signals_changed(); } _session.decode_done(); diff --git a/DSView/pv/view/dsosignal.cpp b/DSView/pv/view/dsosignal.cpp index de5e0bbf..558ce007 100644 --- a/DSView/pv/view/dsosignal.cpp +++ b/DSView/pv/view/dsosignal.cpp @@ -24,6 +24,7 @@ #include +#include "../../extdef.h" #include "dsosignal.h" #include "pv/data/dso.h" #include "pv/data/dsosnapshot.h" @@ -106,13 +107,13 @@ const float DsoSignal::EnvelopeThreshold = 256.0f; const double DsoSignal::TrigMargin = 0.02; const int DsoSignal::UpMargin = 30; -const int DsoSignal::DownMargin = 30; +const int DsoSignal::DownMargin = 0; const int DsoSignal::RightMargin = 30; DsoSignal::DsoSignal(boost::shared_ptr dev_inst, boost::shared_ptr data, const sr_channel * const probe): - Signal(dev_inst, probe, SR_CHANNEL_DSO), + Signal(dev_inst, probe), _data(data), _scale(0), _vDialActive(false), @@ -167,9 +168,14 @@ boost::shared_ptr DsoSignal::data() const return _data; } -void DsoSignal::set_view(pv::view::View *view) +boost::shared_ptr DsoSignal::dso_data() const { - Trace::set_view(view); + return _data; +} + +void DsoSignal::set_viewport(pv::view::Viewport *viewport) +{ + Trace::set_viewport(viewport); update_zeroPos(); const double ms_left = get_view_rect().right() - (MS_RectWidth + MS_RectMargin) * (get_index() + 1); @@ -257,7 +263,7 @@ bool DsoSignal::go_vDialPre() if (_view->session().get_capture_state() == SigSession::Stopped) _scale *= pre_vdiv/_vDial->get_value(); update_zeroPos(); - _view->set_need_update(true); + _view->set_update(_viewport, true); _view->update(); return true; } else { @@ -276,7 +282,7 @@ bool DsoSignal::go_vDialNext() if (_view->session().get_capture_state() == SigSession::Stopped) _scale *= pre_vdiv/_vDial->get_value(); update_zeroPos(); - _view->set_need_update(true); + _view->set_update(_viewport, true); _view->update(); return true; } else { @@ -507,7 +513,7 @@ bool DsoSignal::load_settings() _zero_off = _zeroPos * 255; if (_view) { - _view->set_need_update(true); + _view->set_update(_viewport, true); _view->update(); } return true; @@ -608,6 +614,11 @@ double DsoSignal::get_zeroRate() return _zeroPos; } +double DsoSignal::get_zeroValue() +{ + return _zero_off; +} + void DsoSignal::set_zeroPos(int pos) { if (enabled()) { @@ -644,7 +655,7 @@ void DsoSignal::set_factor(uint64_t factor) _dev_inst->set_config(_probe, NULL, SR_CONF_FACTOR, g_variant_new_uint64(factor)); _vDial->set_factor(factor); - _view->set_need_update(true); + _view->set_update(_viewport, true); _view->update(); } } @@ -668,7 +679,7 @@ uint64_t DsoSignal::get_factor() void DsoSignal::set_ms_show(bool show) { _ms_show = show; - _view->set_need_update(true); + _view->set_update(_viewport, true); } bool DsoSignal::get_ms_show() const @@ -731,10 +742,10 @@ void DsoSignal::update_zeroPos() QRectF DsoSignal::get_view_rect() const { - assert(_view); + assert(_viewport); return QRectF(0, UpMargin, - _view->viewport()->width() - RightMargin, - _view->viewport()->height() - UpMargin - DownMargin); + _viewport->width() - RightMargin, + _viewport->height() - UpMargin - DownMargin); } void DsoSignal::paint_back(QPainter &p, int left, int right) @@ -745,7 +756,9 @@ void DsoSignal::paint_back(QPainter &p, int left, int right) const int height = get_view_rect().height(); const int width = right - left; - p.setPen(Qt::NoPen); + QPen solidPen(Signal::dsFore); + solidPen.setStyle(Qt::SolidLine); + p.setPen(solidPen); p.setBrush(Trace::dsBack); p.drawRect(left, UpMargin, width, height); @@ -772,13 +785,14 @@ void DsoSignal::paint_back(QPainter &p, int left, int right) p.setBrush(Trace::dsBlue); p.drawRect(shown_offset, UpMargin/2 - 3, shown_len, 6); - QPen pen(Signal::dsFore); - pen.setStyle(Qt::DotLine); - p.setPen(pen); + QPen dashPen(Signal::dsFore); + dashPen.setStyle(Qt::DashLine); + p.setPen(dashPen); const double spanY =height * 1.0 / DS_CONF_DSO_VDIVS; for (i = 1; i <= DS_CONF_DSO_VDIVS; i++) { const double posY = spanY * i + UpMargin; - p.drawLine(left, posY, right, posY); + if (i != DS_CONF_DSO_VDIVS) + p.drawLine(left, posY, right, posY); const double miniSpanY = spanY / 5; for (j = 1; j < 5; j++) { p.drawLine(width / 2.0f - 5, posY - miniSpanY * j, @@ -788,8 +802,8 @@ void DsoSignal::paint_back(QPainter &p, int left, int right) const double spanX = width * 1.0 / DS_CONF_DSO_HDIVS; for (i = 1; i <= DS_CONF_DSO_HDIVS; i++) { const double posX = spanX * i; - p.drawLine(posX, UpMargin, - posX, height + UpMargin); + if (i != DS_CONF_DSO_HDIVS) + p.drawLine(posX, UpMargin,posX, height + UpMargin); const double miniSpanX = spanX / 5; for (j = 1; j < 5; j++) { p.drawLine(posX - miniSpanX * j, height / 2.0f + UpMargin - 5, @@ -954,7 +968,7 @@ void DsoSignal::paint_trace(QPainter &p, } p.drawPolyline(points, point - points); - p.eraseRect(get_view_rect().right(), get_view_rect().top(), + p.eraseRect(get_view_rect().right()+1, get_view_rect().top(), _view->viewport()->width() - get_view_rect().width(), get_view_rect().height()); //delete[] samples; @@ -1095,7 +1109,7 @@ bool DsoSignal::mouse_press(int right, const QPoint pt) { int y = get_y(); bool setted = false; - const vector< boost::shared_ptr > traces(_view->get_traces()); + const vector< boost::shared_ptr > traces(_view->get_traces(ALL_VIEW)); const QRectF vDec_rect = get_rect(DSO_VDEC, y, right); const QRectF vInc_rect = get_rect(DSO_VINC, y, right); const QRectF hDec_rect = get_rect(DSO_HDEC, y, right); @@ -1156,7 +1170,7 @@ bool DsoSignal::mouse_wheel(int right, const QPoint pt, const int shift) { int y = get_y(); const vector< boost::shared_ptr > traces( - _view->get_traces()); + _view->get_traces(ALL_VIEW)); bool setted = false; const QRectF vDial_rect = get_rect(DSO_VDIAL, y, right); const QRectF hDial_rect = get_rect(DSO_HDIAL, y, right); @@ -1363,7 +1377,7 @@ void DsoSignal::paint_measure(QPainter &p) bool setted = false; if (_autoH) { - const vector< boost::shared_ptr > traces(_view->get_traces()); + const vector< boost::shared_ptr > traces(_view->get_traces(ALL_VIEW)); const double upPeriod = get_hDialValue() * DS_CONF_DSO_HDIVS * 0.8; const double dnPeriod = get_hDialValue() * DS_CONF_DSO_HDIVS * 0.2; if (_period > upPeriod) { @@ -1404,18 +1418,18 @@ bool DsoSignal::measure(const QPointF &p) { if (_ms_gear_rect.contains(QPoint(p.x(), p.y()))) { _ms_gear_hover = true; - _view->set_need_update(true); + _view->set_update(_viewport, true); return false; } else if (_ms_gear_hover) { - _view->set_need_update(true); + _view->set_update(_viewport, true); _ms_gear_hover = false; } if (_ms_show_rect.contains(QPoint(p.x(), p.y()))) { _ms_show_hover = true; - _view->set_need_update(true); + _view->set_update(_viewport, true); return false; } else if (_ms_show_hover){ - _view->set_need_update(true); + _view->set_update(_viewport, true); _ms_show_hover = false; } diff --git a/DSView/pv/view/dsosignal.h b/DSView/pv/view/dsosignal.h index a2669d8a..032287e6 100644 --- a/DSView/pv/view/dsosignal.h +++ b/DSView/pv/view/dsosignal.h @@ -105,7 +105,8 @@ public: virtual ~DsoSignal(); boost::shared_ptr data() const; - void set_view(pv::view::View *view); + boost::shared_ptr dso_data() const; + void set_viewport(pv::view::Viewport *viewport); void set_scale(float scale); float get_scale(); @@ -154,6 +155,7 @@ public: */ int get_zeroPos(); double get_zeroRate(); + double get_zeroValue(); /** * Sets the mid-Y position of this signal. */ diff --git a/DSView/pv/view/header.cpp b/DSView/pv/view/header.cpp index ca38299f..7ccb9828 100644 --- a/DSView/pv/view/header.cpp +++ b/DSView/pv/view/header.cpp @@ -24,6 +24,7 @@ #include "header.h" #include "view.h" +#include "../../extdef.h" #include "trace.h" #include "dsosignal.h" #include "logicsignal.h" @@ -95,7 +96,7 @@ boost::shared_ptr Header::get_mTrace( { const int w = width(); const vector< boost::shared_ptr > traces( - _view.get_traces()); + _view.get_traces(ALL_VIEW)); BOOST_FOREACH(const boost::shared_ptr t, traces) { @@ -120,7 +121,7 @@ void Header::paintEvent(QPaintEvent*) const int w = width(); const vector< boost::shared_ptr > traces( - _view.get_traces()); + _view.get_traces(ALL_VIEW)); const bool dragging = !_drag_traces.empty(); BOOST_FOREACH(const boost::shared_ptr t, traces) @@ -137,7 +138,7 @@ void Header::mouseDoubleClickEvent(QMouseEvent *event) assert(event); const vector< boost::shared_ptr > traces( - _view.get_traces()); + _view.get_traces(ALL_VIEW)); if (event->button() & Qt::LeftButton) { _mouse_down_point = event->pos(); @@ -161,7 +162,7 @@ void Header::mousePressEvent(QMouseEvent *event) assert(event); const vector< boost::shared_ptr > traces( - _view.get_traces()); + _view.get_traces(ALL_VIEW)); int action; const bool instant = _view.session().get_instant(); if (instant && _view.session().get_capture_state() == SigSession::Running) @@ -229,7 +230,7 @@ void Header::mouseReleaseEvent(QMouseEvent *event) if (action == Trace::COLOR && _colorFlag) { _context_trace = mTrace; changeColor(event); - _view.set_need_update(true); + _view.set_all_update(true); } else if (action == Trace::NAME && _nameFlag) { _context_trace = mTrace; changeName(event); @@ -238,7 +239,7 @@ void Header::mouseReleaseEvent(QMouseEvent *event) if (_moveFlag) { //move(event); _view.signals_changed(); - _view.set_need_update(true); + _view.set_all_update(true); } _colorFlag = false; _nameFlag = false; @@ -253,7 +254,7 @@ void Header::wheelEvent(QWheelEvent *event) if (event->orientation() == Qt::Vertical) { const vector< boost::shared_ptr > traces( - _view.get_traces()); + _view.get_traces(ALL_VIEW)); // Vertical scrolling double shift = event->delta() / 20.0; BOOST_FOREACH(const boost::shared_ptr t, traces) diff --git a/DSView/pv/view/logicsignal.cpp b/DSView/pv/view/logicsignal.cpp index 00df932b..94e96d7b 100644 --- a/DSView/pv/view/logicsignal.cpp +++ b/DSView/pv/view/logicsignal.cpp @@ -52,7 +52,7 @@ const int LogicSignal::StateRound = 5; LogicSignal::LogicSignal(boost::shared_ptr dev_inst, boost::shared_ptr data, const sr_channel * const probe) : - Signal(dev_inst, probe, SR_CHANNEL_LOGIC), + Signal(dev_inst, probe), _data(data), _trig(NONTRIG) { diff --git a/DSView/pv/view/mathtrace.cpp b/DSView/pv/view/mathtrace.cpp index 7aca389c..d8bb16c2 100644 --- a/DSView/pv/view/mathtrace.cpp +++ b/DSView/pv/view/mathtrace.cpp @@ -1,7 +1,488 @@ -#include "ffttrace.h" +/* + * This file is part of the PulseView project. + * + * Copyright (C) 2012 Joel Holdsworth + * + * 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 +#include "mathtrace.h" -FftTrace::FftTrace() +#include +#include + +#include +#include + +#include "../sigsession.h" +#include "../data/dso.h" +#include "../data/dsosnapshot.h" +#include "../view/dsosignal.h" +#include "../view/viewport.h" +#include "../device/devinst.h" + +#include "../data/mathstack.h" + +using namespace boost; +using namespace std; + +namespace pv { +namespace view { + +const int MathTrace::UpMargin = 0; +const int MathTrace::DownMargin = 0; +const int MathTrace::RightMargin = 30; +const QString MathTrace::FFT_ViewMode[2] = { + "Linear RMS", + "DBV RMS" +}; + +const QString MathTrace::FreqPrefixes[9] = + {"", "", "", "", "K", "M", "G", "T", "P"}; +const int MathTrace::FirstSIPrefixPower = -9; +const int MathTrace::LastSIPrefixPower = 15; +const int MathTrace::Pricision = 2; +const int MathTrace::FreqMinorDivNum = 10; +const int MathTrace::TickHeight = 15; +const int MathTrace::VolDivNum = 5; + +const int MathTrace::DbvRanges[4] = { + 100, + 120, + 150, + 200, +}; + +const int MathTrace::HoverPointSize = 3; +const double MathTrace::VerticalRate = 1.0 / 2000.0; + +MathTrace::MathTrace(pv::SigSession &session, + boost::shared_ptr math_stack, int index) : + Trace("FFT("+QString::number(index)+")", index, SR_CHANNEL_FFT), + _session(session), + _math_stack(math_stack), + _enable(false), + _view_mode(0), + _hover_en(false), + _scale(1), + _offset(0) +{ + const vector< boost::shared_ptr > sigs(_session.get_signals()); + for(size_t i = 0; i < sigs.size(); i++) { + const boost::shared_ptr s(sigs[i]); + assert(s); + if (dynamic_pointer_cast(s) && index == s->get_index()) + _colour = s->get_colour(); + } +} + +MathTrace::~MathTrace() { } +bool MathTrace::enabled() const +{ + return _enable; +} + +void MathTrace::set_enable(bool enable) +{ + _enable = enable; +} + +int MathTrace::view_mode() const +{ + return _view_mode; +} + +void MathTrace::set_view_mode(int mode) +{ + assert(mode < sizeof(FFT_ViewMode)/sizeof(FFT_ViewMode[0])); + _view_mode = mode; +} + +std::vector MathTrace::get_view_modes_support() +{ + std::vector modes; + for (int i = 0; i < sizeof(FFT_ViewMode)/sizeof(FFT_ViewMode[0]); i++) { + modes.push_back(FFT_ViewMode[i]); + } + return modes; +} + +const boost::shared_ptr& MathTrace::get_math_stack() const +{ + return _math_stack; +} + +void MathTrace::init_zoom() +{ + _scale = 1; + _offset = 0; +} + +void MathTrace::zoom(double steps, int offset) +{ + if (!_view) + return; + + const int width = get_view_rect().width(); + double pre_offset = _offset + _scale*offset/width; + _scale *= std::pow(3.0/2.0, -steps); + _scale = max(min(_scale, 1.0), 100.0/_math_stack->get_sample_num()); + _offset = pre_offset - _scale*offset/width; + _offset = max(min(_offset, 1-_scale), 0.0); + + _view->set_update(_viewport, true); + _view->update(); +} + +void MathTrace::set_offset(double delta) +{ + int width = get_view_rect().width(); + _offset = _offset + (delta*_scale / width); + _offset = max(min(_offset, 1-_scale), 0.0); + + _view->set_update(_viewport, true); + _view->update(); +} + +double MathTrace::get_offset() const +{ + return _offset; +} + +void MathTrace::set_scale(double scale) +{ + _scale = max(min(scale, 1.0), 100.0/_math_stack->get_sample_num()); + + _view->set_update(_viewport, true); + _view->update(); +} + +double MathTrace::get_scale() const +{ + return _scale; +} + +void MathTrace::set_dbv_range(int range) +{ + _dbv_range = range; +} + +int MathTrace::dbv_range() const +{ + return _dbv_range; +} + +std::vector MathTrace::get_dbv_ranges() +{ + std::vector range; + for (int i = 0; i < sizeof(DbvRanges)/sizeof(DbvRanges[0]); i++) { + range.push_back(DbvRanges[i]); + } + return range; +} + +QString MathTrace::format_freq(double freq, unsigned precision) +{ + if (freq <= 0) { + return "0Hz"; + } else { + const int order = floor(log10f(freq)); + assert(order >= FirstSIPrefixPower); + assert(order <= LastSIPrefixPower); + const int prefix = floor((order - FirstSIPrefixPower)/ 3.0f); + const double divider = pow(10.0, max(prefix * 3.0 + FirstSIPrefixPower, 0.0)); + + QString s; + QTextStream ts(&s); + ts.setRealNumberPrecision(precision); + ts << fixed << freq / divider << + FreqPrefixes[prefix] << "Hz"; + return s; + } +} + +bool MathTrace::measure(const QPointF &p) +{ + _hover_en = false; + if(!_view || !enabled()) + return false; + + const QRectF window = get_view_rect(); + if (!window.contains(p)) + return false; + + const std::vector samples(_math_stack->get_fft_spectrum()); + if(samples.empty()) + return false; + + const int full_size = (_math_stack->get_sample_num()/2+1); + const double view_off = full_size * _offset; + const int view_size = full_size*_scale; + const double sample_per_pixels = view_size/window.width(); + _hover_index = std::round(p.x() * sample_per_pixels + view_off); + + if (_hover_index < full_size) + _hover_en = true; + + //_view->set_update(_viewport, true); + _view->update(); + return true; +} + + +void MathTrace::paint_back(QPainter &p, int left, int right) +{ + if(!_view) + return; + + int i, j; + const int height = get_view_rect().height(); + const int width = right - left; + + QPen solidPen(Signal::dsFore); + solidPen.setStyle(Qt::SolidLine); + p.setPen(solidPen); + p.setBrush(Trace::dsBack); + p.drawRect(left, UpMargin, width, height); +} + +void MathTrace::paint_mid(QPainter &p, int left, int right) +{ + if(!_view) + return; + assert(right >= left); + + if (enabled()) { + const std::vector samples(_math_stack->get_fft_spectrum()); + if(samples.empty()) + return; + + QColor trace_colour = _colour; + trace_colour.setAlpha(150); + p.setPen(trace_colour); + + const int full_size = (_math_stack->get_sample_num()/2+1); + const double view_off = full_size * _offset; + const int view_start = floor(view_off); + const int view_size = full_size*_scale; + QPointF *points = new QPointF[samples.size()]; + QPointF *point = points; + + const bool dc_ignored = _math_stack->dc_ignored(); + const double height = get_view_rect().height(); + const double width = right - left; + const double pixels_per_sample = width/view_size; + + double vdiv; + double vfactor; + double voffset; + BOOST_FOREACH(const boost::shared_ptr s, _session.get_signals()) { + boost::shared_ptr dsoSig; + if (dsoSig = dynamic_pointer_cast(s)) { + if(dsoSig->get_index() == _math_stack->get_index()) { + vdiv = dsoSig->get_vDialValue(); + vfactor = dsoSig->get_factor(); + voffset = dsoSig->get_zeroRate(); + break; + } + } + } + if (_view_mode == 0) { + _vmin = 0; + _vmax = (vdiv*DS_CONF_DSO_HDIVS*vfactor)*VerticalRate; + } else { + _vmax = 20*log10((vdiv*DS_CONF_DSO_HDIVS*vfactor)*VerticalRate); + _vmin = _vmax - _dbv_range; + } + + //const double max_value = *std::max_element(dc_ignored ? ++samples.begin() : samples.begin(), samples.end()); + //const double min_value = *std::min_element(dc_ignored ? ++samples.begin() : samples.begin(), samples.end()); + //_vmax = (_view_mode == 0) ? max_value : 20*log10(max_value); + //_vmin = (_view_mode == 0) ? min_value : 20*log10(min_value); + const double scale = height / (_vmax - _vmin); + + double x = (view_start-view_off)*pixels_per_sample; + uint64_t sample = view_start; + if (dc_ignored && sample == 0) { + sample++; + x += pixels_per_sample; + } + double min_mag = pow(10.0, _vmin/20); + do{ + double mag = samples[sample]; + if (_view_mode != 0) { + if (mag < min_mag) + mag = _vmin; + else + mag = 20*log10(mag); + } + const double y = height - (scale * (mag - _vmin)); + *point++ = QPointF(x, y); + x += pixels_per_sample; + sample++; + }while(x= left); + + const int text_height = p.boundingRect(0, 0, INT_MAX, INT_MAX, + AlignLeft | AlignTop, "8").height(); + const double width = get_view_rect().width(); + const double height = get_view_rect().height(); + double blank_top = 0; + double blank_right = width; + + // horizontal ruler + const double NyFreq = _session.get_device()->get_sample_rate() / (2.0 * _math_stack->get_sample_interval()); + const double deltaFreq = _session.get_device()->get_sample_rate() * 1.0 / + (_math_stack->get_sample_num() * _math_stack->get_sample_interval()); + const double FreqRange = NyFreq * _scale; + const double FreqOffset = NyFreq * _offset; + + const int order = (int)floor(log10(FreqRange)); + const double multiplier = (pow(10.0, order) == FreqRange) ? FreqRange/10 : pow(10.0, order); + const double freq_per_pixel = FreqRange / width; + + p.setPen(Trace::DARK_FORE); + p.setBrush(Qt::NoBrush); + double tick_freq = multiplier * (int)floor(FreqOffset / multiplier); + int division = (int)round(tick_freq * FreqMinorDivNum / multiplier); + double x = (tick_freq - FreqOffset) / freq_per_pixel; + do{ + if (division%FreqMinorDivNum == 0) { + QString freq_str = format_freq(tick_freq); + double typical_width = p.boundingRect(0, 0, INT_MAX, INT_MAX, + AlignLeft | AlignTop, freq_str).width() + 10; + p.drawLine(x, 1, x, TickHeight); + if (x > typical_width/2 && (width-x) > typical_width/2) + p.drawText(x-typical_width/2, TickHeight, typical_width, text_height, + AlignCenter | AlignTop | TextDontClip, freq_str); + } else { + p.drawLine(x, 1, x, TickHeight/2); + } + tick_freq += multiplier/FreqMinorDivNum; + division++; + x = (tick_freq - FreqOffset) / freq_per_pixel; + } while(x < width); + blank_top = max(blank_top, (double)TickHeight + text_height); + + // delta Frequency + QString freq_str = QString::fromWCharArray(L" \u0394") + "Freq: " + format_freq(deltaFreq,4); + p.drawText(0, 0, width, get_view_rect().height(), + AlignRight | AlignBottom | TextDontClip, freq_str); + double delta_left = width-p.boundingRect(0, 0, INT_MAX, INT_MAX, + AlignLeft | AlignTop, freq_str).width(); + blank_right = min(delta_left, blank_right); + + // Vertical ruler + const double vRange = _vmax - _vmin; + const double vOffset = _vmin; + const double vol_per_tick = vRange / VolDivNum; + + p.setPen(Trace::DARK_FORE); + p.setBrush(Qt::NoBrush); + double tick_vol = vol_per_tick + vOffset; + double y = height - height / VolDivNum; + const QString unit = (_view_mode == 0) ? "" : "dbv"; + do{ + if (y > text_height && y < (height - text_height)) { + QString vol_str = QString::number(tick_vol, 'f', Pricision) + unit; + double vol_width = p.boundingRect(0, 0, INT_MAX, INT_MAX, + AlignLeft | AlignTop, vol_str).width(); + p.drawLine(width, y, width-TickHeight/2, y); + p.drawText(width-TickHeight-vol_width, y-text_height/2, vol_width, text_height, + AlignCenter | AlignTop | TextDontClip, vol_str); + blank_right = min(width-TickHeight-vol_width, blank_right); + } + tick_vol += vol_per_tick; + y -= height / VolDivNum; + } while(y > 0); + + // Hover measure + if (_hover_en) { + const std::vector samples(_math_stack->get_fft_spectrum()); + if(samples.empty()) + return; + const int full_size = (_math_stack->get_sample_num()/2+1); + const double view_off = full_size * _offset; + const int view_size = full_size*_scale; + const double scale = height / (_vmax - _vmin); + const double pixels_per_sample = width/view_size; + double x = (_hover_index-view_off)*pixels_per_sample; + double min_mag = pow(10.0, _vmin/20); + _hover_value = samples[_hover_index]; + if (_view_mode != 0) { + if (_hover_value < min_mag) + _hover_value = _vmin; + else + _hover_value = 20*log10(_hover_value); + } + const double y = height - (scale * (_hover_value - _vmin)); + _hover_point = QPointF(x, y); + + p.setPen(QPen(Trace::DARK_FORE, 1, Qt::DashLine)); + p.setBrush(Qt::NoBrush); + p.drawLine(_hover_point.x(), 0, _hover_point.x(), height); + + QString hover_str = QString::number(_hover_value, 'f', 4) + unit + "@" + format_freq(deltaFreq * _hover_index, 4); + const int hover_width = p.boundingRect(0, 0, INT_MAX, INT_MAX, + AlignLeft | AlignTop, hover_str).width(); + const int hover_height = p.boundingRect(0, 0, INT_MAX, INT_MAX, + AlignLeft | AlignTop, hover_str).height(); + QRectF hover_rect(_hover_point.x(), _hover_point.y()-hover_height, hover_width, hover_height); + if (hover_rect.right() > blank_right) + hover_rect.moveRight(min(_hover_point.x(), blank_right)); + if (hover_rect.top() < blank_top) + hover_rect.moveTop(max(_hover_point.y(), blank_top)); + if (hover_rect.top() > 0) + p.drawText(hover_rect, AlignCenter | AlignTop | TextDontClip, hover_str); + + p.setPen(Qt::NoPen); + p.setBrush(Trace::DARK_FORE); + p.drawEllipse(_hover_point, HoverPointSize, HoverPointSize); + } +} + +void MathTrace::paint_type_options(QPainter &p, int right, const QPoint pt) +{ + (void)p; + (void)pt; + (void)right; +} + +QRectF MathTrace::get_view_rect() const +{ + assert(_viewport); + return QRectF(0, UpMargin, + _viewport->width() - RightMargin, + _viewport->height() - UpMargin - DownMargin); +} + +} // namespace view +} // namespace pv diff --git a/DSView/pv/view/mathtrace.h b/DSView/pv/view/mathtrace.h index a64ede6a..7a39babb 100644 --- a/DSView/pv/view/mathtrace.h +++ b/DSView/pv/view/mathtrace.h @@ -1,11 +1,156 @@ -#ifndef FFTTRACE_H -#define FFTTRACE_H +/* + * This file is part of the PulseView project. + * + * Copyright (C) 2012 Joel Holdsworth + * + * 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 + */ +#ifndef DSVIEW_PV_VIEW_MATHTRACE_H +#define DSVIEW_PV_VIEW_MATHTRACE_H -class FftTrace +#include "trace.h" + +#include +#include + +#include + +struct srd_channel; + +namespace pv { + +class SigSession; + +namespace data{ +class MathStack; +} + +namespace view { + +class MathTrace : public Trace { + Q_OBJECT + +private: + static const int UpMargin; + static const int DownMargin; + static const int RightMargin; + static const QString FFT_ViewMode[2]; + + static const QString FreqPrefixes[9]; + static const int FirstSIPrefixPower; + static const int LastSIPrefixPower; + static const int Pricision; + static const int FreqMinorDivNum; + static const int TickHeight; + static const int VolDivNum; + + static const int DbvRanges[4]; + + static const int HoverPointSize; + + static const double VerticalRate; + public: - FftTrace(); + MathTrace(pv::SigSession &session, + boost::shared_ptr math_stack, int index); + ~MathTrace(); + + bool enabled() const; + void set_enable(bool enable); + + void init_zoom(); + void zoom(double steps, int offset); + bool zoom_hit() const; + void set_zoom_hit(bool hit); + + void set_offset(double delta); + double get_offset() const; + + void set_scale(double scale); + double get_scale() const; + + void set_dbv_range(int range); + int dbv_range() const; + std::vector get_dbv_ranges(); + + int view_mode() const; + void set_view_mode(int mode); + std::vector get_view_modes_support(); + + const boost::shared_ptr& get_math_stack() const; + + static QString format_freq(double freq, unsigned precision = Pricision); + + bool measure(const QPointF &p); + + /** + * Paints the background layer of the trace with a QPainter + * @param p the QPainter to paint into. + * @param left the x-coordinate of the left edge of the signal. + * @param right the x-coordinate of the right edge of the signal. + **/ + void paint_back(QPainter &p, int left, int right); + + /** + * Paints the mid-layer of the trace with a QPainter + * @param p the QPainter to paint into. + * @param left the x-coordinate of the left edge of the signal + * @param right the x-coordinate of the right edge of the signal + **/ + void paint_mid(QPainter &p, int left, int right); + + /** + * Paints the foreground layer of the trace with a QPainter + * @param p the QPainter to paint into. + * @param left the x-coordinate of the left edge of the signal + * @param right the x-coordinate of the right edge of the signal + **/ + void paint_fore(QPainter &p, int left, int right); + + QRectF get_view_rect() const; + +protected: + void paint_type_options(QPainter &p, int right, const QPoint pt); + +private: + +private slots: + +private: + pv::SigSession &_session; + boost::shared_ptr _math_stack; + + bool _enable; + int _view_mode; + + double _vmax; + double _vmin; + int _dbv_range; + + uint64_t _hover_index; + bool _hover_en; + QPointF _hover_point; + double _hover_value; + + double _scale; + double _offset; }; -#endif // FFTTRACE_H +} // namespace view +} // namespace pv + +#endif // DSVIEW_PV_VIEW_FFTTRACE_H diff --git a/DSView/pv/view/ruler.cpp b/DSView/pv/view/ruler.cpp index 3fdab4b9..1bbfb340 100644 --- a/DSView/pv/view/ruler.cpp +++ b/DSView/pv/view/ruler.cpp @@ -109,7 +109,7 @@ QString Ruler::format_freq(double period, unsigned precision) QString s; QTextStream ts(&s); ts.setRealNumberPrecision(precision); - ts << fixed << forcesign << 1 / (period * multiplier) << + ts << fixed << 1 / (period * multiplier) << FreqPrefixes[prefix] << "Hz"; return s; } diff --git a/DSView/pv/view/signal.cpp b/DSView/pv/view/signal.cpp index e4cba490..04a4e6ed 100644 --- a/DSView/pv/view/signal.cpp +++ b/DSView/pv/view/signal.cpp @@ -35,8 +35,8 @@ namespace pv { namespace view { Signal::Signal(boost::shared_ptr dev_inst, - const sr_channel *const probe, int type) : - Trace(probe->name, probe->index, type), + const sr_channel *const probe) : + Trace(probe->name, probe->index, probe->type), _dev_inst(dev_inst), _probe(probe) { diff --git a/DSView/pv/view/signal.h b/DSView/pv/view/signal.h index 3ad90f44..632f1e37 100644 --- a/DSView/pv/view/signal.h +++ b/DSView/pv/view/signal.h @@ -57,7 +57,7 @@ private: protected: Signal(boost::shared_ptr dev_inst, - const sr_channel * const probe, int type); + const sr_channel * const probe); /** * Copy constructor diff --git a/DSView/pv/view/trace.cpp b/DSView/pv/view/trace.cpp index 3c79bc1a..661f59f5 100644 --- a/DSView/pv/view/trace.cpp +++ b/DSView/pv/view/trace.cpp @@ -43,7 +43,7 @@ const QColor Trace::dsRed = QColor(213, 15, 37, 255); const QColor Trace::dsGreen = QColor(0, 153, 37, 200); const QColor Trace::dsGray = QColor(0x88, 0x8A, 0x85, 60); const QColor Trace::dsFore = QColor(0xff, 0xff, 0xff, 60); -const QColor Trace::dsBack = QColor(0x16, 0x18, 0x23, 180); +const QColor Trace::dsBack = QColor(0x16, 0x18, 0x23, 200); const QColor Trace::dsDisable = QColor(0x88, 0x8A, 0x85, 200); const QColor Trace::dsActive = QColor(17, 133, 209, 255); const QColor Trace::dsLightBlue = QColor(17, 133, 209, 150); @@ -53,6 +53,7 @@ const QPen Trace::SignalAxisPen = QColor(128, 128, 128, 64); const QColor Trace::DARK_BACK = QColor(48, 47, 47, 255); const QColor Trace::DARK_FORE = QColor(150, 150, 150, 255); const QColor Trace::DARK_HIGHLIGHT = QColor(32, 32, 32, 255); +const QColor Trace::DARK_BLUE = QColor(17, 133, 209, 255); const QColor Trace::PROBE_COLORS[8] = { QColor(0x50, 0x50, 0x50), // Black @@ -187,7 +188,7 @@ void Trace::set_old_v_offset(int v_offset) int Trace::get_zeroPos() { - return _v_offset - _view->v_offset(); + return _v_offset; } int Trace::get_totalHeight() const @@ -211,6 +212,17 @@ pv::view::View* Trace::get_view() const return _view; } +void Trace::set_viewport(pv::view::Viewport *viewport) +{ + assert(viewport); + _viewport = viewport; +} + +pv::view::Viewport* Trace::get_viewport() const +{ + return _viewport; +} + void Trace::paint_back(QPainter &p, int left, int right) { QPen pen(Signal::dsGray); @@ -236,6 +248,9 @@ void Trace::paint_fore(QPainter &p, int left, int right) void Trace::paint_label(QPainter &p, int right, const QPoint pt) { + if (_type == SR_CHANNEL_FFT && !enabled()) + return; + compute_text_size(p); const int y = get_y(); @@ -267,7 +282,7 @@ void Trace::paint_label(QPainter &p, int right, const QPoint pt) }; p.setPen(Qt::transparent); - if (_type == SR_CHANNEL_DSO) { + if (_type == SR_CHANNEL_DSO || _type == SR_CHANNEL_FFT) { p.setBrush((label_rect.contains(pt) || selected()) ? _colour.darker() : _colour); p.drawPolygon(points, countof(points)); } else { @@ -284,6 +299,8 @@ void Trace::paint_label(QPainter &p, int right, const QPoint pt) p.drawText(label_rect, Qt::AlignCenter | Qt::AlignVCenter, "A"); else if (_type == SR_CHANNEL_DECODER) p.drawText(label_rect, Qt::AlignCenter | Qt::AlignVCenter, "D"); + else if (_type == SR_CHANNEL_FFT) + p.drawText(label_rect, Qt::AlignCenter | Qt::AlignVCenter, "M"); else p.drawText(label_rect, Qt::AlignCenter | Qt::AlignVCenter, QString::number(_index_list.front())); } @@ -355,7 +372,7 @@ QRectF Trace::get_view_rect() const int Trace::get_y() const { - return _v_offset - _view->v_offset(); + return _v_offset; } QColor Trace::get_text_colour() const diff --git a/DSView/pv/view/trace.h b/DSView/pv/view/trace.h index 26d9b504..1c30de77 100644 --- a/DSView/pv/view/trace.h +++ b/DSView/pv/view/trace.h @@ -31,6 +31,8 @@ #include +#include + #include "selectableitem.h" #include "dsldial.h" @@ -40,6 +42,7 @@ namespace pv { namespace view { class View; +class Viewport; class Trace : public SelectableItem { @@ -73,6 +76,7 @@ public: static const QColor DARK_BACK; static const QColor DARK_FORE; static const QColor DARK_HIGHLIGHT; + static const QColor DARK_BLUE; static const QColor PROBE_COLORS[8]; @@ -166,6 +170,8 @@ public: virtual void set_view(pv::view::View *view); pv::view::View* get_view() const; + virtual void set_viewport(pv::view::Viewport *viewport); + pv::view::Viewport* get_viewport() const; /** * Paints the background layer of the trace with a QPainter @@ -290,6 +296,7 @@ signals: protected: pv::view::View *_view; + pv::view::Viewport *_viewport; QString _name; QColor _colour; diff --git a/DSView/pv/view/view.cpp b/DSView/pv/view/view.cpp index df0e2e87..43cf5868 100644 --- a/DSView/pv/view/view.cpp +++ b/DSView/pv/view/view.cpp @@ -41,6 +41,7 @@ #include "dsosignal.h" #include "view.h" #include "viewport.h" +#include "mathtrace.h" #include "../device/devinst.h" #include "pv/sigsession.h" @@ -68,25 +69,19 @@ const QColor View::CursorAreaColour(220, 231, 243); const QSizeF View::LabelPadding(4, 4); View::View(SigSession &session, pv::toolbars::SamplingBar *sampling_bar, QWidget *parent) : - QAbstractScrollArea(parent), + QScrollArea(parent), _session(session), _sampling_bar(sampling_bar), - _viewport(new Viewport(*this)), - _ruler(new Ruler(*this)), - _header(new Header(*this)), - _devmode(new DevMode(this, session)), _scale(1e-8), _preScale(1e-6), _maxscale(1e9), _minscale(1e-15), _offset(0), _preOffset(0), - _v_offset(0), _updating_scroll(false), - _need_update(false), _show_cursors(false), _trig_pos(0), - _hover_point(-1, -1) + _hover_point(-1, -1) { setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn); @@ -95,8 +90,60 @@ View::View(SigSession &session, pv::toolbars::SamplingBar *sampling_bar, QWidget connect(verticalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(v_scroll_value_changed(int))); + // 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; + + _active_viewport = NULL; + _ruler = new Ruler(*this); + _header = new Header(*this); + _devmode = new DevMode(this, session); + setViewportMargins(headerWidth(), RulerHeight, 0, 0); - setViewport(_viewport); + //setViewport(_viewport); + + // windows splitter + _time_viewport = new Viewport(*this, TIME_VIEW); + _time_viewport->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); + _time_viewport->setMinimumHeight(100); + connect(_time_viewport, SIGNAL(measure_updated()), + this, SLOT(on_measure_updated())); + _fft_viewport = new Viewport(*this, FFT_VIEW); + _fft_viewport->setVisible(false); + _fft_viewport->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); + _fft_viewport->setMinimumHeight(100); + connect(_fft_viewport, SIGNAL(measure_updated()), + this, SLOT(on_measure_updated())); + + _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->setContentsMargins(0,0,0,0); + _viewcenter->setLayout(layout); + layout->addWidget(_vsplitter, 0, 0); + QWidget* bottom = new QWidget(this); + bottom->setFixedHeight(10); + layout->addWidget(bottom, 1, 0); + setViewport(_viewcenter); + connect(_vsplitter, SIGNAL(splitterMoved(int,int)), + this, SLOT(splitterMoved(int, int))); connect(&_session, SIGNAL(device_setted()), _devmode, SLOT(set_device())); @@ -117,12 +164,13 @@ View::View(SigSession &session, pv::toolbars::SamplingBar *sampling_bar, QWidget connect(_header, SIGNAL(header_updated()), this, SLOT(header_updated())); - _viewport->installEventFilter(this); + _time_viewport->installEventFilter(this); + _fft_viewport->installEventFilter(this); _ruler->installEventFilter(this); _header->installEventFilter(this); _devmode->installEventFilter(this); - _viewport->setObjectName(tr("ViewArea_viewport")); + _viewcenter->setObjectName(tr("ViewArea_center")); _ruler->setObjectName(tr("ViewArea_ruler")); _header->setObjectName(tr("ViewArea_header")); @@ -148,11 +196,6 @@ double View::offset() const return _offset; } -int View::v_offset() const -{ - return _v_offset; -} - double View::get_minscale() const { return _minscale; @@ -168,14 +211,15 @@ void View::zoom(double steps) zoom(steps, get_view_width() / 2); } -void View::set_need_update(bool need_update) +void View::set_update(Viewport *viewport, bool need_update) { - _need_update = need_update; + viewport->set_need_update(need_update); } -bool View::need_update() const +void View::set_all_update(bool need_update) { - return _need_update; + BOOST_FOREACH(Viewport *viewport, _viewport_list) + viewport->set_need_update(need_update); } void View::update_sample(bool instant) @@ -235,7 +279,7 @@ void View::zoom(double steps, int offset) if (_scale != _preScale || _offset != _preOffset) { _header->update(); _ruler->update(); - _viewport->update(); + viewport_update(); update_scroll(); } //} @@ -255,7 +299,7 @@ void View::set_scale_offset(double scale, double offset) update_scroll(); _header->update(); _ruler->update(); - _viewport->update(); + viewport_update(); } //} } @@ -269,25 +313,57 @@ void View::set_preScale_preOffset() set_scale_offset(_preScale, _preOffset); } -vector< boost::shared_ptr > View::get_traces() const +vector< boost::shared_ptr > View::get_traces(int type) { +// const vector< boost::shared_ptr > sigs(_session.get_signals()); +// const vector< boost::shared_ptr > groups(_session.get_group_signals()); +//#ifdef ENABLE_DECODE +// const vector< boost::shared_ptr > decode_sigs( +// _session.get_decode_signals()); +// vector< boost::shared_ptr > traces( +// sigs.size() + groups.size() + decode_sigs.size()); +//#else +// vector< boost::shared_ptr > traces(sigs.size() + groups.size()); +//#endif + +// vector< boost::shared_ptr >::iterator i = traces.begin(); +// i = copy(sigs.begin(), sigs.end(), i); +//#ifdef ENABLE_DECODE +// i = copy(decode_sigs.begin(), decode_sigs.end(), i); +//#endif +// i = copy(groups.begin(), groups.end(), i); + +// stable_sort(traces.begin(), traces.end(), compare_trace_v_offsets); +// return traces; + const vector< boost::shared_ptr > sigs(_session.get_signals()); const vector< boost::shared_ptr > groups(_session.get_group_signals()); #ifdef ENABLE_DECODE const vector< boost::shared_ptr > decode_sigs( _session.get_decode_signals()); - vector< boost::shared_ptr > traces( - sigs.size() + groups.size() + decode_sigs.size()); -#else - vector< boost::shared_ptr > traces(sigs.size() + groups.size()); #endif + const vector< boost::shared_ptr > maths(_session.get_math_signals()); - vector< boost::shared_ptr >::iterator i = traces.begin(); - i = copy(sigs.begin(), sigs.end(), i); + vector< boost::shared_ptr > traces; + BOOST_FOREACH(boost::shared_ptr t, sigs) { + if (type == ALL_VIEW || _trace_view_map[t->get_type()] == type) + traces.push_back(t); + } #ifdef ENABLE_DECODE - i = copy(decode_sigs.begin(), decode_sigs.end(), i); + BOOST_FOREACH(boost::shared_ptr t, decode_sigs) { + if (type == ALL_VIEW || _trace_view_map[t->get_type()] == type) + traces.push_back(t); + } #endif - i = copy(groups.begin(), groups.end(), i); + BOOST_FOREACH(boost::shared_ptr t, groups) { + if (type == ALL_VIEW || _trace_view_map[t->get_type()] == type) + traces.push_back(t); + } + + BOOST_FOREACH(boost::shared_ptr t, maths) { + if (type == ALL_VIEW || _trace_view_map[t->get_type()] == type) + traces.push_back(t); + } stable_sort(traces.begin(), traces.end(), compare_trace_v_offsets); return traces; @@ -323,21 +399,21 @@ void View::show_cursors(bool show) { _show_cursors = show; _ruler->update(); - _viewport->update(); + viewport_update(); } void View::show_trig_cursor(bool show) { _show_trig_cursor = show; _ruler->update(); - _viewport->update(); + viewport_update(); } void View::show_search_cursor(bool show) { _show_search_cursor = show; _ruler->update(); - _viewport->update(); + viewport_update(); } void View::set_trig_pos(quint64 trig_pos) @@ -353,7 +429,7 @@ void View::set_trig_pos(quint64 trig_pos) _trigger_time = _trigger_time.addSecs(secs); _ruler->update(); - _viewport->update(); + viewport_update(); } void View::set_search_pos(uint64_t search_pos) @@ -365,7 +441,7 @@ void View::set_search_pos(uint64_t search_pos) _search_cursor->set_index(search_pos); set_scale_offset(_scale, time - _scale * get_view_width() / 2); _ruler->update(); - _viewport->update(); + viewport_update(); } uint64_t View::get_trig_pos() @@ -385,7 +461,7 @@ const QPointF& View::hover_point() const void View::normalize_layout() { - const vector< boost::shared_ptr > traces(get_traces()); + const vector< boost::shared_ptr > traces(get_traces(ALL_VIEW)); int v_min = INT_MAX; BOOST_FOREACH(const boost::shared_ptr t, traces) @@ -395,7 +471,7 @@ void View::normalize_layout() BOOST_FOREACH(boost::shared_ptr t, traces) t->set_v_offset(t->get_v_offset() + delta); - verticalScrollBar()->setSliderPosition(_v_offset + delta); + verticalScrollBar()->setSliderPosition(delta); v_scroll_value_changed(verticalScrollBar()->sliderPosition()); } @@ -422,9 +498,9 @@ void View::get_scroll_layout(double &length, double &offset) const void View::update_scroll() { - assert(_viewport); + assert(_viewcenter); - const QSize areaSize = _viewport->size(); + const QSize areaSize = _viewcenter->size(); // Set the horizontal scroll bar double length = 0, offset = 0; @@ -448,8 +524,7 @@ void View::update_scroll() // Set the vertical scrollbar verticalScrollBar()->setPageStep(areaSize.height()); - verticalScrollBar()->setRange(0, - _viewport->get_total_height() - areaSize.height()); + verticalScrollBar()->setRange(0,0); } void View::update_scale() @@ -473,47 +548,93 @@ void View::update_scale() _trig_cursor->set_index(_trig_pos); _ruler->update(); - _viewport->update(); + viewport_update(); } void View::signals_changed() { int total_rows = 0; uint8_t max_height = MaxHeightUnit; - const vector< boost::shared_ptr > traces(get_traces()); - BOOST_FOREACH(const boost::shared_ptr t, traces) - { - assert(t); - if (dynamic_pointer_cast(t) || - t->enabled()) - total_rows += t->rows_size(); + vector< boost::shared_ptr > time_traces; + vector< boost::shared_ptr > fft_traces; + BOOST_FOREACH(const boost::shared_ptr 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); } - const double height = (_viewport->height() - - horizontalScrollBar()->height() - - 2 * SignalMargin * traces.size()) * 1.0 / total_rows; - - if (_session.get_device()->dev_inst()->mode == LOGIC) { - GVariant* gvar = _session.get_device()->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 (!fft_traces.empty()) { + if (!_fft_viewport->isVisible()) { + _fft_viewport->setVisible(true); + _fft_viewport->clear_measure(); + _viewport_list.push_back(_fft_viewport); + _vsplitter->refresh(); + } + BOOST_FOREACH(boost::shared_ptr 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()); } - _signalHeight = (int)((height <= 0) ? 1 : (height >= max_height) ? max_height : height); } else { - _signalHeight = (int)((height <= 0) ? 1 : height); - } - _spanY = _signalHeight + 2 * SignalMargin; - int next_v_offset = SignalMargin; - BOOST_FOREACH(boost::shared_ptr t, traces) { - t->set_view(this); - const double traceHeight = _signalHeight*t->rows_size(); - t->set_totalHeight((int)traceHeight); - t->set_v_offset(next_v_offset + 0.5 * traceHeight + SignalMargin); - next_v_offset += traceHeight + 2 * SignalMargin; - } + _fft_viewport->setVisible(false); + _vsplitter->refresh(); + + // Find the decoder in the stack + std::list< Viewport *>::iterator iter = _viewport_list.begin(); + for(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) { + BOOST_FOREACH(const boost::shared_ptr t, time_traces) { + assert(t); + if (dynamic_pointer_cast(t) || + t->enabled()) + total_rows += t->rows_size(); + } + + const double height = (_time_viewport->height() + - horizontalScrollBar()->height() + - 2 * SignalMargin * time_traces.size()) * 1.0 / total_rows; + + if (_session.get_device()->dev_inst()->mode == LOGIC) { + GVariant* gvar = _session.get_device()->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); + } + _signalHeight = (int)((height <= 0) ? 1 : (height >= max_height) ? max_height : height); + } else if (_session.get_device()->dev_inst()->mode == DSO) { + _signalHeight = (_header->height() + - horizontalScrollBar()->height() + - 2 * SignalMargin * time_traces.size()) * 1.0 / total_rows; + } else { + _signalHeight = (int)((height <= 0) ? 1 : height); + } + _spanY = _signalHeight + 2 * SignalMargin; + int next_v_offset = SignalMargin; + BOOST_FOREACH(boost::shared_ptr t, time_traces) { + t->set_view(this); + t->set_viewport(_time_viewport); + const double traceHeight = _signalHeight*t->rows_size(); + t->set_totalHeight((int)traceHeight); + t->set_v_offset(next_v_offset + 0.5 * traceHeight + SignalMargin); + next_v_offset += traceHeight + 2 * SignalMargin; + + boost::shared_ptr dsoSig; + if (dsoSig = dynamic_pointer_cast(t)) + dsoSig->set_scale(dsoSig->get_view_rect().height() / 256.0f); + } + _time_viewport->clear_measure(); + } - _viewport->clear_measure(); header_updated(); normalize_layout(); data_updated(); @@ -525,7 +646,7 @@ bool View::eventFilter(QObject *object, QEvent *event) if (type == QEvent::MouseMove) { const QMouseEvent *const mouse_event = (QMouseEvent*)event; - if (object == _ruler || object == _viewport) { + if (object == _ruler || object == _time_viewport || object == _fft_viewport) { //_hover_point = QPoint(mouse_event->x(), 0); double cur_periods = (mouse_event->pos().x() * _scale + _offset) / _ruler->get_min_period(); double integer_x = (round(cur_periods) * _ruler->get_min_period() - _offset ) / _scale; @@ -575,7 +696,7 @@ int View::headerWidth() QFont font = QApplication::font(); QFontMetrics fm(font); - const vector< boost::shared_ptr > traces(get_traces()); + const vector< boost::shared_ptr > traces(get_traces(ALL_VIEW)); if (!traces.empty()){ BOOST_FOREACH(const boost::shared_ptr t, traces) { maxNameWidth = max(fm.boundingRect(t->get_name()).width(), maxNameWidth); @@ -593,8 +714,10 @@ int View::headerWidth() void View::resizeEvent(QResizeEvent*) { + setViewportMargins(headerWidth(), RulerHeight, 0, 0); update_margins(); update_scroll(); + signals_changed(); if (_session.get_device()->dev_inst()->mode == DSO) _scale = _session.get_device()->get_time_base() * std::pow(10.0, -9.0) * DS_CONF_DSO_HDIVS / get_view_width(); @@ -605,10 +728,10 @@ void View::resizeEvent(QResizeEvent*) _scale = min(_scale, _maxscale); - signals_changed(); _ruler->update(); _header->header_resize(); - _need_update = true; + set_update(_time_viewport, true); + set_update(_fft_viewport, true); } void View::h_scroll_value_changed(int value) @@ -631,15 +754,14 @@ void View::h_scroll_value_changed(int value) if (_offset != _preOffset) { _ruler->update(); - _viewport->update(); + viewport_update(); } } void View::v_scroll_value_changed(int value) { - _v_offset = value; _header->update(); - _viewport->update(); + viewport_update(); } void View::data_updated() @@ -651,18 +773,19 @@ void View::data_updated() update_scroll(); // Repaint the view - _need_update = true; - _viewport->update(); + set_update(_time_viewport, true); + set_update(_fft_viewport, true); + viewport_update(); } void View::update_margins() { - _ruler->setGeometry(_viewport->x(), 0, - get_view_width(), _viewport->y()); - _header->setGeometry(0, _viewport->y(), - _viewport->x(), _viewport->height()); + _ruler->setGeometry(_viewcenter->x(), 0, + get_view_width(), _viewcenter->y()); + _header->setGeometry(0, _viewcenter->y(), + _viewcenter->x(), _viewcenter->height()); _devmode->setGeometry(0, 0, - _viewport->x(), _viewport->y()); + _viewcenter->x(), _viewcenter->y()); } void View::header_updated() @@ -673,21 +796,21 @@ void View::header_updated() // Update the scroll bars update_scroll(); - _viewport->update(); + viewport_update(); _header->update(); } void View::marker_time_changed() { _ruler->update(); - _viewport->update(); + viewport_update(); } void View::on_traces_moved() { update_scroll(); - _need_update = true; - _viewport->update(); + set_update(_time_viewport, true); + viewport_update(); //traces_moved(); } @@ -740,9 +863,18 @@ void View::set_cursor_middle(int index) set_scale_offset(_scale, (*i)->index() * 1.0 / _session.get_device()->get_sample_rate() - _scale * get_view_width() / 2); } -Viewport * View::get_viewport() +void View::on_measure_updated() { - return _viewport; + _active_viewport = dynamic_cast(sender()); + measure_updated(); +} + +QString View::get_measure(QString option) +{ + if (_active_viewport) { + return _active_viewport->get_measure(option); + } + return "#####"; } QString View::get_cm_time(int index) @@ -782,13 +914,16 @@ void View::on_cursor_moved() void View::set_measure_en(int enable) { - _viewport->set_measure_en(enable); + BOOST_FOREACH(Viewport *viewport, _viewport_list) + viewport->set_measure_en(enable); } void View::on_state_changed(bool stop) { - if (stop) - _viewport->stop_trigger_timer(); + if (stop) { + BOOST_FOREACH(Viewport *viewport, _viewport_list) + viewport->stop_trigger_timer(); + } } int View::get_view_width() @@ -800,7 +935,7 @@ int View::get_view_width() view_width = max((double)view_width, s->get_view_rect().width()); } } else { - view_width = _viewport->width(); + view_width = _viewcenter->width(); } return view_width; @@ -815,7 +950,7 @@ int View::get_view_height() view_height = max((double)view_height, s->get_view_rect().height()); } } else { - view_height = _viewport->width(); + view_height = _viewcenter->height(); } return view_height; @@ -846,5 +981,19 @@ void View::show_region(uint64_t start, uint64_t end) set_scale_offset(new_scale, new_off); } +void View::viewport_update() +{ + _viewcenter->update(); + BOOST_FOREACH(Viewport *viewport, _viewport_list) + viewport->update(); +} + +void View::splitterMoved(int pos, int index) +{ + (void)pos; + (void)index; + signals_changed(); +} + } // namespace view } // namespace pv diff --git a/DSView/pv/view/view.h b/DSView/pv/view/view.h index 1f903737..a90f8fb4 100644 --- a/DSView/pv/view/view.h +++ b/DSView/pv/view/view.h @@ -32,12 +32,15 @@ #include #include -#include +#include #include #include +#include +#include "../../extdef.h" #include "../toolbars/samplingbar.h" #include "../data/signaldata.h" +#include "../view/viewport.h" #include "cursor.h" #include "signal.h" @@ -57,7 +60,7 @@ class Ruler; class Trace; class Viewport; -class View : public QAbstractScrollArea { +class View : public QScrollArea { Q_OBJECT private: @@ -111,7 +114,7 @@ public: void set_scale_offset(double scale, double offset); void set_preScale_preOffset(); - std::vector< boost::shared_ptr > get_traces() const; + std::vector< boost::shared_ptr > get_traces(int type); /** * Returns true if cursors are displayed. false otherwise. @@ -164,11 +167,10 @@ public: double get_minscale() const; double get_maxscale() const; - void set_need_update(bool need_update); - bool need_update() const; + void set_update(Viewport *viewport, bool need_update); + void set_all_update(bool need_update); uint64_t get_cursor_samples(int index); - Viewport * get_viewport(); QString get_cm_time(int index); QString get_cm_delta(int index1, int index2); @@ -187,6 +189,11 @@ public: QString trigger_time(); + QString get_measure(QString option); + + void viewport_update(); + + signals: void hover_point_changed(); @@ -198,6 +205,8 @@ signals: void mode_changed(); + void measure_updated(); + private: void get_scroll_layout(double &length, double &offset) const; @@ -236,12 +245,22 @@ private slots: void set_trig_pos(quint64 trig_pos); + void on_measure_updated(); + + void splitterMoved(int pos, int index); + private: SigSession &_session; pv::toolbars::SamplingBar *_sampling_bar; - Viewport *_viewport; + QWidget *_viewcenter; + QSplitter *_vsplitter; + Viewport * _time_viewport; + Viewport * _fft_viewport; + Viewport *_active_viewport; + std::list _viewport_list; + std::map _trace_view_map; Ruler *_ruler; Header *_header; DevMode *_devmode; @@ -259,10 +278,7 @@ private: int _spanY; int _signalHeight; - int _v_offset; - bool _updating_scroll; - - bool _need_update; + bool _updating_scroll; bool _show_cursors; diff --git a/DSView/pv/view/viewport.cpp b/DSView/pv/view/viewport.cpp index 4f5b6f67..4ecbc294 100644 --- a/DSView/pv/view/viewport.cpp +++ b/DSView/pv/view/viewport.cpp @@ -21,13 +21,13 @@ */ -#include "view.h" #include "viewport.h" #include "ruler.h" #include "signal.h" #include "dsosignal.h" #include "logicsignal.h" +#include "mathtrace.h" #include "../device/devinst.h" #include "../data/logic.h" #include "../data/logicsnapshot.h" @@ -52,9 +52,11 @@ namespace view { const double Viewport::DragDamping = 1.05; const double Viewport::MinorDragRateUp = 10; -Viewport::Viewport(View &parent) : - QWidget(&parent), - _view(parent), +Viewport::Viewport(View &parent, View_type type) : + QWidget(&parent), + _view(parent), + _type(type), + _need_update(false), _total_receive_len(0), _action_type(NO_ACTION), _measure_type(NO_MEASURE), @@ -100,7 +102,7 @@ int Viewport::get_total_height() const { int h = 0; - const vector< boost::shared_ptr > traces(_view.get_traces()); + const vector< boost::shared_ptr > traces(_view.get_traces(_type)); BOOST_FOREACH(const boost::shared_ptr t, traces) { assert(t); h += (int)(t->get_totalHeight()); @@ -126,11 +128,13 @@ void Viewport::paintEvent(QPaintEvent *event) QPainter p(this); style()->drawPrimitive(QStyle::PE_Widget, &o, &p, this); - const vector< boost::shared_ptr > traces(_view.get_traces()); + const vector< boost::shared_ptr > traces(_view.get_traces(_type)); BOOST_FOREACH(const boost::shared_ptr t, traces) { assert(t); t->paint_back(p, 0, _view.get_view_width()); + if (t->enabled() && _view.session().get_device()->dev_inst()->mode == DSO) + break; } p.setRenderHint(QPainter::Antialiasing, false); @@ -145,9 +149,11 @@ void Viewport::paintEvent(QPaintEvent *event) break; case SigSession::Running: - p.setRenderHint(QPainter::Antialiasing); - paintProgress(p); - p.setRenderHint(QPainter::Antialiasing, false); + if (_type == TIME_VIEW) { + p.setRenderHint(QPainter::Antialiasing); + paintProgress(p); + p.setRenderHint(QPainter::Antialiasing, false); + } break; } } else { @@ -172,11 +178,11 @@ void Viewport::paintEvent(QPaintEvent *event) void Viewport::paintSignals(QPainter &p) { - const vector< boost::shared_ptr > traces(_view.get_traces()); + const vector< boost::shared_ptr > traces(_view.get_traces(_type)); if (_view.scale() != _curScale || _view.offset() != _curOffset || _view.get_signalHeight() != _curSignalHeight || - _view.need_update()) { + _need_update) { _curScale = _view.scale(); _curOffset = _view.offset(); _curSignalHeight = _view.get_signalHeight(); @@ -190,15 +196,14 @@ void Viewport::paintSignals(QPainter &p) { assert(t); if (t->enabled()) - t->paint_mid(dbp, 0, _view.get_view_width()); + t->paint_mid(dbp, 0, t->get_view_rect().width()); } - - _view.set_need_update(false); + _need_update = false; } p.drawPixmap(0, 0, pixmap); // plot cursors - if (_view.cursors_shown()) { + if (_view.cursors_shown() && _type == TIME_VIEW) { list::iterator i = _view.get_cursorList().begin(); double cursorX; const double samples_per_pixel = _view.session().get_device()->get_sample_rate() * _view.scale(); @@ -212,22 +217,25 @@ void Viewport::paintSignals(QPainter &p) i++; } } - if (_view.trig_cursor_shown()) { - _view.get_trig_cursor()->paint(p, rect(), 0); - } - if (_view.search_cursor_shown()) { - _view.get_search_cursor()->paint(p, rect(), 0); - } - // plot zoom rect - if (_action_type == LOGIC_ZOOM) { - p.setPen(Qt::NoPen); - p.setBrush(Trace::dsLightBlue); - p.drawRect(QRectF(_mouse_down_point, _mouse_point)); - } + if (_type == TIME_VIEW) { + if (_view.trig_cursor_shown()) { + _view.get_trig_cursor()->paint(p, rect(), 0); + } + if (_view.search_cursor_shown()) { + _view.get_search_cursor()->paint(p, rect(), 0); + } - //plot measure arrow - paintMeasure(p); + // plot zoom rect + if (_action_type == LOGIC_ZOOM) { + p.setPen(Qt::NoPen); + p.setBrush(Trace::dsLightBlue); + p.drawRect(QRectF(_mouse_down_point, _mouse_point)); + } + + //plot measure arrow + paintMeasure(p); + } } void Viewport::paintProgress(QPainter &p) @@ -413,38 +421,51 @@ void Viewport::mouseMoveEvent(QMouseEvent *event) _hover_hit = false; if (event->buttons() & Qt::LeftButton) { - _view.set_scale_offset(_view.scale(), - _mouse_down_offset + - (_mouse_down_point - event->pos()).x() * - _view.scale()); - _drag_strength = (_mouse_down_point - event->pos()).x(); + if (_type == TIME_VIEW) { + _view.set_scale_offset(_view.scale(), + _mouse_down_offset + + (_mouse_down_point - event->pos()).x() * + _view.scale()); + _drag_strength = (_mouse_down_point - event->pos()).x(); + } else if (_type == FFT_VIEW) { + BOOST_FOREACH(const boost::shared_ptr t, _view.session().get_math_signals()) { + assert(t); + if(t->enabled()) { + double delta = (_mouse_point - event->pos()).x(); + t->set_offset(delta); + break; + } + } + } } - if (!(event->buttons() || Qt::NoButton)) { - if (_action_type == DSO_TRIG_MOVE) { - if (_drag_sig) { - boost::shared_ptr dsoSig; - if (dsoSig = dynamic_pointer_cast(_drag_sig)) - dsoSig->set_trig_vpos(event->pos().y()); + if (_type == TIME_VIEW) { + if (!(event->buttons() || Qt::NoButton)) { + if (_action_type == DSO_TRIG_MOVE) { + if (_drag_sig) { + boost::shared_ptr dsoSig; + if (dsoSig = dynamic_pointer_cast(_drag_sig)) + dsoSig->set_trig_vpos(event->pos().y()); + } } - } - if (_action_type == CURS_MOVE) { - uint64_t sample_rate = _view.session().get_device()->get_sample_rate(); - TimeMarker* grabbed_marker = _view.get_ruler()->get_grabbed_cursor(); - if (_view.cursors_shown() && grabbed_marker) { - const double cur_time = _view.offset() + _view.hover_point().x() * _view.scale(); - const double pos = cur_time * sample_rate; - const double pos_delta = pos - (uint64_t)pos; - if ( pos_delta < 0.5) - grabbed_marker->set_index((uint64_t)floor(pos)); - else - grabbed_marker->set_index((uint64_t)ceil(pos)); + if (_action_type == CURS_MOVE) { + uint64_t sample_rate = _view.session().get_device()->get_sample_rate(); + TimeMarker* grabbed_marker = _view.get_ruler()->get_grabbed_cursor(); + if (_view.cursors_shown() && grabbed_marker) { + const double cur_time = _view.offset() + _view.hover_point().x() * _view.scale(); + const double pos = cur_time * sample_rate; + const double pos_delta = pos - (uint64_t)pos; + if ( pos_delta < 0.5) + grabbed_marker->set_index((uint64_t)floor(pos)); + else + grabbed_marker->set_index((uint64_t)ceil(pos)); + } } - } - if (_action_type == DSO_YM) - _dso_ym_end = event->pos().y(); + if (_action_type == DSO_YM) + _dso_ym_end = event->pos().y(); + } } _mouse_point = event->pos(); @@ -457,177 +478,178 @@ void Viewport::mouseReleaseEvent(QMouseEvent *event) { assert(event); - if ((_action_type == NO_ACTION) && - (event->button() == Qt::LeftButton)) { - // priority 0 - if (_action_type == NO_ACTION && _view.cursors_shown()) { - list::iterator i = _view.get_cursorList().begin(); - double cursorX; - const double samples_per_pixel = _view.session().get_device()->get_sample_rate() * _view.scale(); - while (i != _view.get_cursorList().end()) { - cursorX = (*i)->index()/samples_per_pixel - (_view.offset() / _view.scale()); - if ((*i)->grabbed()) { - _view.get_ruler()->rel_grabbed_cursor(); - } else if (qAbs(cursorX - event->pos().x()) <= HitCursorMargin) { - _view.get_ruler()->set_grabbed_cursor(*i); - _action_type = CURS_MOVE; - break; - } - i++; - } - } - - if (_view.session().get_device()->dev_inst()->mode == LOGIC && - _view.session().get_capture_state() == SigSession::Stopped) { - // priority 1 - if (_action_type == NO_ACTION) { - const double strength = _drag_strength*DragTimerInterval*1.0/_time.elapsed(); - if (_time.elapsed() < 200 && - abs(_drag_strength) < MinorDragOffsetUp && - abs(strength) > MinorDragRateUp) { - _drag_strength = _drag_strength; - _drag_timer.start(DragTimerInterval); - _action_type = LOGIC_MOVE; - } else if (_time.elapsed() < 200 && - abs(strength) > DragTimerInterval) { - _drag_strength = strength * 5; - _drag_timer.start(DragTimerInterval); - _action_type = LOGIC_MOVE; + if (_type == TIME_VIEW) { + if ((_action_type == NO_ACTION) && + (event->button() == Qt::LeftButton)) { + // priority 0 + if (_action_type == NO_ACTION && _view.cursors_shown()) { + list::iterator i = _view.get_cursorList().begin(); + double cursorX; + const double samples_per_pixel = _view.session().get_device()->get_sample_rate() * _view.scale(); + while (i != _view.get_cursorList().end()) { + cursorX = (*i)->index()/samples_per_pixel - (_view.offset() / _view.scale()); + if ((*i)->grabbed()) { + _view.get_ruler()->rel_grabbed_cursor(); + } else if (qAbs(cursorX - event->pos().x()) <= HitCursorMargin) { + _view.get_ruler()->set_grabbed_cursor(*i); + _action_type = CURS_MOVE; + break; + } + i++; } } - // priority 2 - if (_action_type == NO_ACTION) { - if (_mouse_down_point.x() == event->pos().x()) { + if (_view.session().get_device()->dev_inst()->mode == LOGIC && + _view.session().get_capture_state() == SigSession::Stopped) { + // priority 1 + if (_action_type == NO_ACTION) { + const double strength = _drag_strength*DragTimerInterval*1.0/_time.elapsed(); + if (_time.elapsed() < 200 && + abs(_drag_strength) < MinorDragOffsetUp && + abs(strength) > MinorDragRateUp) { + _drag_strength = _drag_strength; + _drag_timer.start(DragTimerInterval); + _action_type = LOGIC_MOVE; + } else if (_time.elapsed() < 200 && + abs(strength) > DragTimerInterval) { + _drag_strength = strength * 5; + _drag_timer.start(DragTimerInterval); + _action_type = LOGIC_MOVE; + } + } + + // priority 2 + if (_action_type == NO_ACTION) { + if (_mouse_down_point.x() == event->pos().x()) { + const vector< boost::shared_ptr > sigs(_view.session().get_signals()); + BOOST_FOREACH(const boost::shared_ptr s, sigs) { + assert(s); + if (abs(event->pos().y() - s->get_y()) < _view.get_signalHeight()) { + _action_type = LOGIC_EDGE; + _edge_start = (_view.offset() + (event->pos().x() + 0.5) * _view.scale()) * _view.session().get_device()->get_sample_rate(); + break; + } + } + } + } + } else if (_view.session().get_device()->dev_inst()->mode == DSO) { + // priority 0 + if (_action_type == NO_ACTION && _hover_hit) { + _action_type = DSO_YM; + _dso_ym_valid = true; + _dso_ym_sig_index = _hover_sig_index; + _dso_ym_sig_value = _hover_sig_value; + _dso_ym_index = _hover_index; + _dso_ym_start = event->pos().y(); + } + + // priority 1 + if (_action_type == NO_ACTION) { const vector< boost::shared_ptr > sigs(_view.session().get_signals()); BOOST_FOREACH(const boost::shared_ptr s, sigs) { assert(s); - if (abs(event->pos().y() - s->get_y()) < _view.get_signalHeight()) { - _action_type = LOGIC_EDGE; - _edge_start = (_view.offset() + (event->pos().x() + 0.5) * _view.scale()) * _view.session().get_device()->get_sample_rate(); - break; + if (!s->enabled()) + continue; + boost::shared_ptr dsoSig; + if (dsoSig = dynamic_pointer_cast(s)) { + if (dsoSig->get_trig_rect(0, _view.get_view_width()).contains(_mouse_point)) { + _drag_sig = s; + _action_type = DSO_TRIG_MOVE; + break; + } } } } } - } else if (_view.session().get_device()->dev_inst()->mode == DSO) { - // priority 0 - if (_action_type == NO_ACTION && _hover_hit) { - _action_type = DSO_YM; - _dso_ym_valid = true; - _dso_ym_sig_index = _hover_sig_index; - _dso_ym_sig_value = _hover_sig_value; - _dso_ym_index = _hover_index; - _dso_ym_start = event->pos().y(); + } else if (_action_type == DSO_YM) { + if (event->button() == Qt::LeftButton) { + _dso_ym_end = event->pos().y(); + _action_type = NO_ACTION; + } else if (event->button() == Qt::RightButton) { + _action_type = NO_ACTION; + _dso_ym_valid = false; } + } else if (_action_type == DSO_TRIG_MOVE) { + if (event->button() == Qt::LeftButton) { + _drag_sig.reset(); + _action_type = NO_ACTION; + } + } else if (_action_type == DSO_XM_STEP0) { + if (event->button() == Qt::LeftButton) { + _action_type = DSO_XM_STEP1; + _dso_xm_valid = true; + } + } else if (_action_type == DSO_XM_STEP1) { + if (event->button() == Qt::LeftButton) { + const uint64_t sample_rate = _view.session().get_device()->get_sample_rate(); + const double scale = _view.scale(); + const double samples_per_pixel = sample_rate * scale; - // priority 1 - if (_action_type == NO_ACTION) { - const vector< boost::shared_ptr > sigs(_view.session().get_signals()); - BOOST_FOREACH(const boost::shared_ptr s, sigs) { - assert(s); - if (!s->enabled()) - continue; - boost::shared_ptr dsoSig; - if (dsoSig = dynamic_pointer_cast(s)) { - if (dsoSig->get_trig_rect(0, _view.get_view_width()).contains(_mouse_point)) { - _drag_sig = s; - _action_type = DSO_TRIG_MOVE; - break; - } + _dso_xm_index[1] = event->pos().x() * samples_per_pixel + _view.offset() * sample_rate; + const uint64_t max_index = max(_dso_xm_index[0], _dso_xm_index[1]); + _dso_xm_index[0] = min(_dso_xm_index[0], _dso_xm_index[1]); + _dso_xm_index[1] = max_index; + + _action_type = DSO_XM_STEP2; + } else if (event->button() == Qt::RightButton) { + _action_type = NO_ACTION; + _dso_xm_valid = false; + _mm_width = "#####"; + _mm_period = "#####"; + _mm_freq = "#####"; + _mm_duty = "#####"; + measure_updated(); + } + } else if (_action_type == DSO_XM_STEP2) { + if (event->button() == Qt::LeftButton) { + const uint64_t sample_rate = _view.session().get_device()->get_sample_rate(); + const double scale = _view.scale(); + const double samples_per_pixel = sample_rate * scale; + _dso_xm_index[2] = event->pos().x() * samples_per_pixel + _view.offset() * sample_rate; + const uint64_t max_index = max(_dso_xm_index[1], _dso_xm_index[2]); + _dso_xm_index[1] = min(_dso_xm_index[1], _dso_xm_index[2]); + _dso_xm_index[2] = max_index; + + _action_type = NO_ACTION; + } else if (event->button() == Qt::RightButton) { + _action_type = NO_ACTION; + _dso_xm_valid = false; + _mm_width = "#####"; + _mm_period = "#####"; + _mm_freq = "#####"; + _mm_duty = "#####"; + measure_updated(); + } + } else if (_action_type == CURS_MOVE) { + _action_type = NO_ACTION; + if (_view.cursors_shown()) { + list::iterator i = _view.get_cursorList().begin(); + while (i != _view.get_cursorList().end()) { + if ((*i)->grabbed()) { + _view.get_ruler()->rel_grabbed_cursor(); } + i++; } } - } - } else if (_action_type == DSO_YM) { - if (event->button() == Qt::LeftButton) { - _dso_ym_end = event->pos().y(); + } else if (_action_type == LOGIC_EDGE) { _action_type = NO_ACTION; - } else if (event->button() == Qt::RightButton) { + _edge_rising = 0; + _edge_falling = 0; + } else if (_action_type == LOGIC_MOVE) { + _drag_strength = 0; + _drag_timer.stop(); _action_type = NO_ACTION; - _dso_ym_valid = false; - } - } else if (_action_type == DSO_TRIG_MOVE) { - if (event->button() == Qt::LeftButton) { - _drag_sig.reset(); - _action_type = NO_ACTION; - } - } else if (_action_type == DSO_XM_STEP0) { - if (event->button() == Qt::LeftButton) { - _action_type = DSO_XM_STEP1; - _dso_xm_valid = true; - } - } else if (_action_type == DSO_XM_STEP1) { - if (event->button() == Qt::LeftButton) { - const uint64_t sample_rate = _view.session().get_device()->get_sample_rate(); - const double scale = _view.scale(); - const double samples_per_pixel = sample_rate * scale; - - _dso_xm_index[1] = event->pos().x() * samples_per_pixel + _view.offset() * sample_rate; - const uint64_t max_index = max(_dso_xm_index[0], _dso_xm_index[1]); - _dso_xm_index[0] = min(_dso_xm_index[0], _dso_xm_index[1]); - _dso_xm_index[1] = max_index; - - _action_type = DSO_XM_STEP2; - } else if (event->button() == Qt::RightButton) { - _action_type = NO_ACTION; - _dso_xm_valid = false; - _mm_width = "#####"; - _mm_period = "#####"; - _mm_freq = "#####"; - _mm_duty = "#####"; - measure_updated(); - } - } else if (_action_type == DSO_XM_STEP2) { - if (event->button() == Qt::LeftButton) { - const uint64_t sample_rate = _view.session().get_device()->get_sample_rate(); - const double scale = _view.scale(); - const double samples_per_pixel = sample_rate * scale; - _dso_xm_index[2] = event->pos().x() * samples_per_pixel + _view.offset() * sample_rate; - const uint64_t max_index = max(_dso_xm_index[1], _dso_xm_index[2]); - _dso_xm_index[1] = min(_dso_xm_index[1], _dso_xm_index[2]); - _dso_xm_index[2] = max_index; - - _action_type = NO_ACTION; - } else if (event->button() == Qt::RightButton) { - _action_type = NO_ACTION; - _dso_xm_valid = false; - _mm_width = "#####"; - _mm_period = "#####"; - _mm_freq = "#####"; - _mm_duty = "#####"; - measure_updated(); - } - } else if (_action_type == CURS_MOVE) { - _action_type = NO_ACTION; - if (_view.cursors_shown()) { - list::iterator i = _view.get_cursorList().begin(); - while (i != _view.get_cursorList().end()) { - if ((*i)->grabbed()) { - _view.get_ruler()->rel_grabbed_cursor(); - } - i++; + } else if (_action_type == LOGIC_ZOOM) { + if (event->pos().x() != _mouse_down_point.x()) { + const double newOffset = _view.offset() + (min(event->pos().x(), _mouse_down_point.x()) + 0.5) * _view.scale(); + const double newScale = max(min(_view.scale() * abs(event->pos().x() - _mouse_down_point.x()) / _view.get_view_width(), + _view.get_maxscale()), _view.get_minscale()); + if (newScale != _view.scale()) + _view.set_scale_offset(newScale, newOffset); } + _action_type = NO_ACTION; } - } else if (_action_type == LOGIC_EDGE) { - _action_type = NO_ACTION; - _edge_rising = 0; - _edge_falling = 0; - } else if (_action_type == LOGIC_MOVE) { - _drag_strength = 0; - _drag_timer.stop(); - _action_type = NO_ACTION; - } else if (_action_type == LOGIC_ZOOM) { - if (event->pos().x() != _mouse_down_point.x()) { - const double newOffset = _view.offset() + (min(event->pos().x(), _mouse_down_point.x()) + 0.5) * _view.scale(); - const double newScale = max(min(_view.scale() * abs(event->pos().x() - _mouse_down_point.x()) / _view.get_view_width(), - _view.get_maxscale()), _view.get_minscale()); - if (newScale != _view.scale()) - _view.set_scale_offset(newScale, newOffset); - } - _action_type = NO_ACTION; } - update(); } @@ -676,16 +698,26 @@ void Viewport::wheelEvent(QWheelEvent *event) { assert(event); - if (event->orientation() == Qt::Vertical) { - // Vertical scrolling is interpreted as zooming in/out - const double offset = event->x(); - _view.zoom(event->delta() / 80, offset); - } else if (event->orientation() == Qt::Horizontal) { - // Horizontal scrolling is interpreted as moving left/right - _view.set_scale_offset(_view.scale(), - event->delta() * _view.scale() - + _view.offset()); - } + if (_type == FFT_VIEW) { + BOOST_FOREACH(const boost::shared_ptr t, _view.session().get_math_signals()) { + assert(t); + if(t->enabled()) { + t->zoom(event->delta() / 80, event->x()); + break; + } + } + } else if (_type == TIME_VIEW){ + if (event->orientation() == Qt::Vertical) { + // Vertical scrolling is interpreted as zooming in/out + const double offset = event->x(); + _view.zoom(event->delta() / 80, offset); + } else if (event->orientation() == Qt::Horizontal) { + // Horizontal scrolling is interpreted as moving left/right + _view.set_scale_offset(_view.scale(), + event->delta() * _view.scale() + + _view.offset()); + } + } measure(); } @@ -759,62 +791,72 @@ void Viewport::clear_measure() void Viewport::measure() { _measure_type = NO_MEASURE; - const uint64_t sample_rate = _view.session().get_device()->get_sample_rate(); - const vector< boost::shared_ptr > sigs(_view.session().get_signals()); - BOOST_FOREACH(const boost::shared_ptr s, sigs) { - assert(s); - boost::shared_ptr logicSig; - boost::shared_ptr dsoSig; - if (logicSig = dynamic_pointer_cast(s)) { - if (_action_type == NO_ACTION) { - if (logicSig->measure(_mouse_point, _cur_sample, _nxt_sample, _thd_sample)) { - _measure_type = LOGIC_FREQ; + if (_type == TIME_VIEW) { + const uint64_t sample_rate = _view.session().get_device()->get_sample_rate(); + const vector< boost::shared_ptr > sigs(_view.session().get_signals()); + BOOST_FOREACH(const boost::shared_ptr s, sigs) { + assert(s); + boost::shared_ptr logicSig; + boost::shared_ptr dsoSig; + if (logicSig = dynamic_pointer_cast(s)) { + if (_action_type == NO_ACTION) { + if (logicSig->measure(_mouse_point, _cur_sample, _nxt_sample, _thd_sample)) { + _measure_type = LOGIC_FREQ; - _mm_width = _view.get_ruler()->format_real_time(_nxt_sample - _cur_sample, sample_rate); - _mm_period = _thd_sample != 0 ? _view.get_ruler()->format_real_time(_thd_sample - _cur_sample, sample_rate) : "#####"; - _mm_freq = _thd_sample != 0 ? _view.get_ruler()->format_real_freq(_thd_sample - _cur_sample, sample_rate) : "#####"; + _mm_width = _view.get_ruler()->format_real_time(_nxt_sample - _cur_sample, sample_rate); + _mm_period = _thd_sample != 0 ? _view.get_ruler()->format_real_time(_thd_sample - _cur_sample, sample_rate) : "#####"; + _mm_freq = _thd_sample != 0 ? _view.get_ruler()->format_real_freq(_thd_sample - _cur_sample, sample_rate) : "#####"; - const double pixels_offset = _view.offset() / _view.scale(); - const double samples_per_pixel = sample_rate * _view.scale(); - _cur_preX = _cur_sample / samples_per_pixel - pixels_offset; - _cur_aftX = _nxt_sample / samples_per_pixel - pixels_offset; - _cur_thdX = _thd_sample / samples_per_pixel - pixels_offset; - _cur_midY = logicSig->get_y(); + const double pixels_offset = _view.offset() / _view.scale(); + const double samples_per_pixel = sample_rate * _view.scale(); + _cur_preX = _cur_sample / samples_per_pixel - pixels_offset; + _cur_aftX = _nxt_sample / samples_per_pixel - pixels_offset; + _cur_thdX = _thd_sample / samples_per_pixel - pixels_offset; + _cur_midY = logicSig->get_y(); - _mm_duty = _thd_sample != 0 ? QString::number((_nxt_sample - _cur_sample) * 100.0 / (_thd_sample - _cur_sample), 'f', 2)+"%" : - "#####"; + _mm_duty = _thd_sample != 0 ? QString::number((_nxt_sample - _cur_sample) * 100.0 / (_thd_sample - _cur_sample), 'f', 2)+"%" : + "#####"; + break; + } else { + _measure_type = NO_MEASURE; + _mm_width = "#####"; + _mm_period = "#####"; + _mm_freq = "#####"; + _mm_duty = "#####"; + } + } else if (_action_type == LOGIC_EDGE) { + if (logicSig->edges(_view.hover_point(), _edge_start, _edge_rising, _edge_falling)) { + const double pixels_offset = _view.offset() / _view.scale(); + const double samples_per_pixel = sample_rate * _view.scale(); + _cur_preX = _edge_start / samples_per_pixel - pixels_offset; + _cur_aftX = _view.hover_point().x(); + _cur_midY = logicSig->get_y() - logicSig->get_totalHeight()/2 - 5; + + _em_rising = "Rising: " + QString::number(_edge_rising); + _em_falling = "Falling: " + QString::number(_edge_falling); + _em_edges = "Edges: " + QString::number(_edge_rising + _edge_falling); + + break; + } + } + } else if (dsoSig = dynamic_pointer_cast(s)) { + if (_measure_en && dsoSig->measure(_view.hover_point())) { + _measure_type = DSO_VALUE; break; } else { _measure_type = NO_MEASURE; - _mm_width = "#####"; - _mm_period = "#####"; - _mm_freq = "#####"; - _mm_duty = "#####"; - } - } else if (_action_type == LOGIC_EDGE) { - if (logicSig->edges(_view.hover_point(), _edge_start, _edge_rising, _edge_falling)) { - const double pixels_offset = _view.offset() / _view.scale(); - const double samples_per_pixel = sample_rate * _view.scale(); - _cur_preX = _edge_start / samples_per_pixel - pixels_offset; - _cur_aftX = _view.hover_point().x(); - _cur_midY = logicSig->get_y() - logicSig->get_totalHeight()/2 - 5; - - _em_rising = "Rising: " + QString::number(_edge_rising); - _em_falling = "Falling: " + QString::number(_edge_falling); - _em_edges = "Edges: " + QString::number(_edge_rising + _edge_falling); - - break; } } - } else if (dsoSig = dynamic_pointer_cast(s)) { - if (_measure_en && dsoSig->measure(_view.hover_point())) { - _measure_type = DSO_VALUE; - break; - } else { - _measure_type = NO_MEASURE; + } + } else if (_type == FFT_VIEW) { + BOOST_FOREACH(const boost::shared_ptr t, _view.session().get_math_signals()) { + assert(t); + if(t->enabled()) { + t->measure(_view.hover_point()); } } } + measure_updated(); } @@ -1065,7 +1107,7 @@ void Viewport::paintMeasure(QPainter &p) p.drawLine(x[dso_xm_stage-1], _dso_xm_y, _mouse_point.x(), _dso_xm_y); p.drawLine(_mouse_point.x(), 0, - _mouse_point.x(), _view.get_viewport()->height()); + _mouse_point.x(), height()); } measure_updated(); } @@ -1179,5 +1221,11 @@ void Viewport::paintTrigTime(QPainter &p) } } +void Viewport::set_need_update(bool update) +{ + _need_update = update; +} + + } // namespace view } // namespace pv diff --git a/DSView/pv/view/viewport.h b/DSView/pv/view/viewport.h index 999c72a9..33ed45b2 100644 --- a/DSView/pv/view/viewport.h +++ b/DSView/pv/view/viewport.h @@ -30,8 +30,12 @@ #include #include #include + #include +#include "../../extdef.h" +#include "../view/view.h" + class QPainter; class QPaintEvent; class SigSession; @@ -81,7 +85,7 @@ public: }; public: - explicit Viewport(View &parent); + explicit Viewport(View &parent, View_type type); int get_total_height() const; @@ -96,6 +100,8 @@ public: void clear_measure(); + void set_need_update(bool update); + protected: void paintEvent(QPaintEvent *event); @@ -125,6 +131,8 @@ signals: private: View &_view; + View_type _type; + bool _need_update; uint64_t _total_receive_len; QPoint _mouse_point; diff --git a/libsigrok4DSL/hardware/demo/demo.c b/libsigrok4DSL/hardware/demo/demo.c index a31467a5..d39aebf0 100644 --- a/libsigrok4DSL/hardware/demo/demo.c +++ b/libsigrok4DSL/hardware/demo/demo.c @@ -247,6 +247,9 @@ static const uint64_t samplerates[] = { // SR_MB(4), // SR_MB(8), // SR_MB(16), +// SR_MB(32), +// SR_MB(64), +// SR_MB(128), //}; static const uint64_t samplecounts[] = { @@ -270,10 +273,10 @@ static const uint64_t samplecounts[] = { /* We name the probes 0-7 on our demo driver. */ static const char *probe_names[NUM_PROBES + 1] = { - "Channel 0", "Channel 1", "Channel 2", "Channel 3", - "Channel 4", "Channel 5", "Channel 6", "Channel 7", - "Channel 8", "Channel 9", "Channel 10", "Channel 11", - "Channel 12", "Channel 13", "Channel 14", "Channel 15", + "CH0", "CH1", "CH2", "CH3", + "CH4", "CH5", "CH6", "CH7", + "CH8", "CH9", "CH10", "CH11", + "CH12", "CH13", "CH14", "CH15", NULL, }; diff --git a/libsigrok4DSL/libsigrok.h b/libsigrok4DSL/libsigrok.h index e55adbb0..c6c9e01e 100644 --- a/libsigrok4DSL/libsigrok.h +++ b/libsigrok4DSL/libsigrok.h @@ -549,6 +549,7 @@ enum { SR_CHANNEL_ANALOG, SR_CHANNEL_GROUP, SR_CHANNEL_DECODER, + SR_CHANNEL_FFT, }; enum { @@ -560,7 +561,7 @@ enum { struct sr_channel { /* The index field will go: use g_slist_length(sdi->channels) instead. */ uint16_t index; - int type; + int type; gboolean enabled; char *name; char *trigger; From 1b6f16bf9596da63e29a779307aff05bc65e2fae Mon Sep 17 00:00:00 2001 From: DreamSourceLab Date: Sun, 15 May 2016 22:51:18 +0800 Subject: [PATCH 15/32] Improve decode progress and content display @ LA mode --- DSView/pv/data/decoderstack.cpp | 2 +- DSView/pv/dock/protocoldock.cpp | 33 ++++++++++- DSView/pv/dock/protocoldock.h | 2 + DSView/pv/dock/triggerdock.cpp | 10 ++-- DSView/pv/dock/triggerdock.h | 3 + DSView/pv/mainwindow.cpp | 19 +++++-- DSView/pv/mainwindow.h | 5 ++ DSView/pv/sigsession.cpp | 85 +++++++++++++++++++++++----- DSView/pv/sigsession.h | 10 +++- DSView/pv/storesession.cpp | 9 ++- DSView/pv/toolbars/samplingbar.cpp | 21 +------ DSView/pv/toolbars/samplingbar.h | 1 - DSView/pv/view/cursor.cpp | 8 +-- DSView/pv/view/decodetrace.cpp | 23 ++++++-- DSView/pv/view/decodetrace.h | 6 ++ DSView/pv/view/dsosignal.cpp | 12 ++-- DSView/pv/view/mathtrace.cpp | 4 +- DSView/pv/view/ruler.cpp | 6 +- DSView/pv/view/timemarker.cpp | 2 +- DSView/pv/view/view.cpp | 45 ++++++++------- DSView/pv/view/view.h | 2 +- DSView/pv/view/viewport.cpp | 30 +++++----- libsigrok4DSL/hardware/DSL/dslogic.c | 56 +++++++++--------- libsigrok4DSL/proto.h | 1 + libsigrok4DSL/trigger.c | 10 ++++ 25 files changed, 273 insertions(+), 132 deletions(-) diff --git a/DSView/pv/data/decoderstack.cpp b/DSView/pv/data/decoderstack.cpp index e52a91f4..e9ac125d 100644 --- a/DSView/pv/data/decoderstack.cpp +++ b/DSView/pv/data/decoderstack.cpp @@ -332,6 +332,7 @@ void DecoderStack::clear() _sample_count = 0; _frame_complete = false; _samples_decoded = 0; + //new_decode_data(); _error_message = QString(); for (map::const_iterator i = _rows.begin(); i != _rows.end(); i++) @@ -468,7 +469,6 @@ void DecoderStack::decode_data( } _options_changed = false; decode_done(); - //new_decode_data(); } void DecoderStack::decode_proc() diff --git a/DSView/pv/dock/protocoldock.cpp b/DSView/pv/dock/protocoldock.cpp index 637022e1..087a63bb 100644 --- a/DSView/pv/dock/protocoldock.cpp +++ b/DSView/pv/dock/protocoldock.cpp @@ -200,6 +200,7 @@ void ProtocolDock::add_protocol() _set_button->setIcon(QIcon::fromTheme("protocol", QIcon(":/icons/gear.png"))); QLabel *_protocol_label = new QLabel(_up_widget); + QLabel *_progress_label = new QLabel(_up_widget); _del_button->setCheckable(true); _protocol_label->setText(_protocol_combobox->currentText()); @@ -212,17 +213,23 @@ void ProtocolDock::add_protocol() _del_button_list.push_back(_del_button); _set_button_list.push_back(_set_button); _protocol_label_list.push_back(_protocol_label); + _progress_label_list.push_back(_progress_label); _protocol_index_list.push_back(_protocol_combobox->currentIndex()); QHBoxLayout *hori_layout = new QHBoxLayout(); hori_layout->addWidget(_set_button); hori_layout->addWidget(_del_button); hori_layout->addWidget(_protocol_label); + hori_layout->addWidget(_progress_label); hori_layout->addStretch(1); _hori_layout_list.push_back(hori_layout); _up_layout->insertLayout(_del_button_list.size(), hori_layout); - //_session.add_protocol_analyzer(_protocol_combobox->currentIndex(), _sel_probes, _options, _options_index); + // progress connection + const std::vector< boost::shared_ptr > decode_sigs( + _session.get_decode_signals()); + //connect(decode_sigs.back().get(), SIGNAL(decoded_progress(int)), this, SLOT(decoded_progess(int))); + protocol_updated(); } } @@ -265,6 +272,7 @@ void ProtocolDock::del_protocol() delete _del_button_list.at(del_index); delete _set_button_list.at(del_index); delete _protocol_label_list.at(del_index); + delete _progress_label_list.at(del_index); _session.remove_decode_signal(0); del_index++; @@ -273,6 +281,7 @@ void ProtocolDock::del_protocol() _del_button_list.clear(); _set_button_list.clear(); _protocol_label_list.clear(); + _progress_label_list.clear(); _protocol_index_list.clear(); } else { QMessageBox msg(this); @@ -293,11 +302,13 @@ void ProtocolDock::del_protocol() delete _del_button_list.at(del_index); delete _set_button_list.at(del_index); delete _protocol_label_list.at(del_index); + delete _progress_label_list.at(del_index); _hori_layout_list.remove(del_index); _del_button_list.remove(del_index); _set_button_list.remove(del_index); _protocol_label_list.remove(del_index); + _progress_label_list.remove(del_index); _protocol_index_list.remove(del_index); _session.remove_decode_signal(del_index); @@ -320,6 +331,7 @@ void ProtocolDock::del_all_protocol() delete _del_button_list.at(del_index); delete _set_button_list.at(del_index); delete _protocol_label_list.at(del_index); + delete _progress_label_list.at(del_index); _session.remove_decode_signal(0); del_index++; @@ -328,12 +340,31 @@ void ProtocolDock::del_all_protocol() _del_button_list.clear(); _set_button_list.clear(); _protocol_label_list.clear(); + _progress_label_list.clear(); _protocol_index_list.clear(); protocol_updated(); } } +void ProtocolDock::decoded_progess(int progress) +{ + (void) progress; + + const std::vector< boost::shared_ptr > decode_sigs( + _session.get_decode_signals()); + int index = 0; + BOOST_FOREACH(boost::shared_ptr d, decode_sigs) { + QString progress_str = QString::number(d->get_progress()) + "%"; + if (d->get_progress() == 100) + _progress_label_list.at(index)->setStyleSheet("color:green;"); + else + _progress_label_list.at(index)->setStyleSheet("color:red;"); + _progress_label_list.at(index)->setText(progress_str); + index++; + } +} + void ProtocolDock::set_model() { pv::dialogs::ProtocolList *protocollist_dlg = new pv::dialogs::ProtocolList(this, _session); diff --git a/DSView/pv/dock/protocoldock.h b/DSView/pv/dock/protocoldock.h index 96d31124..8ca0d198 100644 --- a/DSView/pv/dock/protocoldock.h +++ b/DSView/pv/dock/protocoldock.h @@ -72,6 +72,7 @@ private slots: void add_protocol(); void rst_protocol(); void del_protocol(); + void decoded_progess(int progress); void set_model(); void update_model(); void export_table_view(); @@ -96,6 +97,7 @@ private: QVector _del_button_list; QVector _set_button_list; QVector _protocol_label_list; + QVector _progress_label_list; QVector _protocol_index_list; QVector _hori_layout_list; QVBoxLayout *_up_layout; diff --git a/DSView/pv/dock/triggerdock.cpp b/DSView/pv/dock/triggerdock.cpp index dc32ad8a..6dc4ca6d 100644 --- a/DSView/pv/dock/triggerdock.cpp +++ b/DSView/pv/dock/triggerdock.cpp @@ -38,6 +38,8 @@ namespace pv { namespace dock { +const int TriggerDock::MinTrigPosition = 1; + TriggerDock::TriggerDock(QWidget *parent, SigSession &session) : QScrollArea(parent), _session(session) @@ -56,10 +58,10 @@ TriggerDock::TriggerDock(QWidget *parent, SigSession &session) : position_label = new QLabel(tr("Trigger Position: "), _widget); position_spinBox = new QSpinBox(_widget); - position_spinBox->setRange(0, 99); + position_spinBox->setRange(MinTrigPosition, 99); position_spinBox->setButtonSymbols(QAbstractSpinBox::NoButtons); position_slider = new QSlider(Qt::Horizontal, _widget); - position_slider->setRange(0, 99); + position_slider->setRange(MinTrigPosition, 99); connect(position_slider, SIGNAL(valueChanged(int)), position_spinBox, SLOT(setValue(int))); connect(position_spinBox, SIGNAL(valueChanged(int)), position_slider, SLOT(setValue(int))); @@ -356,8 +358,8 @@ void TriggerDock::device_change() maxRange = 99; else maxRange = max_hd_depth*70 / sample_limits; - position_spinBox->setRange(0, maxRange); - position_slider->setRange(0, maxRange); + position_spinBox->setRange(MinTrigPosition, maxRange); + position_slider->setRange(MinTrigPosition, maxRange); diff --git a/DSView/pv/dock/triggerdock.h b/DSView/pv/dock/triggerdock.h index 4ec977a3..ece32316 100644 --- a/DSView/pv/dock/triggerdock.h +++ b/DSView/pv/dock/triggerdock.h @@ -55,6 +55,9 @@ class TriggerDock : public QScrollArea { Q_OBJECT +private: + static const int MinTrigPosition; + public: TriggerDock(QWidget *parent, SigSession &session); ~TriggerDock(); diff --git a/DSView/pv/mainwindow.cpp b/DSView/pv/mainwindow.cpp index 0806142a..57ca57f2 100644 --- a/DSView/pv/mainwindow.cpp +++ b/DSView/pv/mainwindow.cpp @@ -112,7 +112,7 @@ void MainWindow::setup_ui() { setObjectName(QString::fromUtf8("MainWindow")); setMinimumHeight(680); - setMinimumWidth(300); + setMinimumWidth(500); resize(1024, 768); // Set the window icon @@ -192,8 +192,6 @@ void MainWindow::setup_ui() SLOT(run_stop())); connect(_sampling_bar, SIGNAL(instant_stop()), this, SLOT(instant_stop())); - connect(_sampling_bar, SIGNAL(update_scale()), _view, - SLOT(update_scale()), Qt::DirectConnection); connect(_sampling_bar, SIGNAL(sample_count_changed()), _trigger_widget, SLOT(device_change())); connect(_dso_trigger_widget, SIGNAL(set_trig_pos(quint64)), _view, @@ -248,6 +246,8 @@ void MainWindow::setup_ui() SLOT(test_data_error())); connect(&_session, SIGNAL(malloc_error()), this, SLOT(malloc_error())); + connect(&_session, SIGNAL(hardware_connect_failed()), this, + SLOT(hardware_connect_failed())); connect(_view, SIGNAL(cursor_update()), _measure_widget, SLOT(cursor_update())); @@ -471,6 +471,17 @@ void MainWindow::malloc_error() msg.exec(); } +void MainWindow::hardware_connect_failed() +{ + _session.stop_capture(); + QMessageBox msg(this); + msg.setText(tr("Hardware Connect Failed")); + msg.setInformativeText(tr("Please check hardware connection!")); + msg.setStandardButtons(QMessageBox::Ok); + msg.setIcon(QMessageBox::Warning); + msg.exec(); +} + void MainWindow::capture_state_changed(int state) { _file_bar->enable_toggle(state != SigSession::Running); @@ -492,7 +503,7 @@ void MainWindow::capture_state_changed(int state) if (gvar != NULL) { uint64_t actual_samples = g_variant_get_uint64(gvar); g_variant_unref(gvar); - if (actual_samples != _session.get_device()->get_sample_limit()) { + if (actual_samples != _session.cur_samplelimits()) { show_session_error(tr("RLE Mode Warning"), tr("Hardware buffer is full!\nActually received samples is less than setted sample depth!")); } diff --git a/DSView/pv/mainwindow.h b/DSView/pv/mainwindow.h index 0ae69138..36abdf6f 100644 --- a/DSView/pv/mainwindow.h +++ b/DSView/pv/mainwindow.h @@ -131,6 +131,11 @@ private slots: void device_attach(); void device_detach(); + /* + * errors + */ + void hardware_connect_failed(); + private: DeviceManager &_device_manager; diff --git a/DSView/pv/sigsession.cpp b/DSView/pv/sigsession.cpp index e0b443df..a3851d27 100644 --- a/DSView/pv/sigsession.cpp +++ b/DSView/pv/sigsession.cpp @@ -151,6 +151,8 @@ void SigSession::set_device(boost::shared_ptr dev_inst) throw(Q if (_dev_inst) { try { _dev_inst->use(this); + _cur_samplerate = _dev_inst->get_sample_rate(); + _cur_samplelimits = _dev_inst->get_sample_limit(); } catch(const QString e) { throw(e); return; @@ -423,6 +425,24 @@ SigSession::capture_state SigSession::get_capture_state() const return _capture_state; } +uint64_t SigSession::cur_samplelimits() const +{ + return _cur_samplelimits; +} + +uint64_t SigSession::cur_samplerate() const +{ + return _cur_samplerate; +} + +double SigSession::cur_sampletime() const +{ + if (_cur_samplerate == 0) + return 0; + else + return _cur_samplelimits * 1.0 / _cur_samplerate; +} + void SigSession::start_capture(bool instant, boost::function error_handler) { @@ -582,36 +602,60 @@ void SigSession::sample_thread_proc(boost::shared_ptr dev_inst, assert(!_cur_analog_snapshot); } -void SigSession::read_sample_rate(const sr_dev_inst *const sdi) +void SigSession::update_data_header(const sr_dev_inst *const sdi) { GVariant *gvar; - uint64_t sample_rate = 0; + int ret; // Read out the sample rate if(sdi->driver) { - const int ret = sr_config_get(sdi->driver, sdi, NULL, NULL, SR_CONF_SAMPLERATE, &gvar); + ret = sr_config_get(sdi->driver, sdi, NULL, NULL, SR_CONF_SAMPLERATE, &gvar); if (ret != SR_OK) { - qDebug("Failed to get samplerate\n"); + hardware_connect_failed(); return; } - sample_rate = g_variant_get_uint64(gvar); + _cur_samplerate = g_variant_get_uint64(gvar); + g_variant_unref(gvar); + + ret = sr_config_get(sdi->driver, sdi, NULL, NULL, SR_CONF_LIMIT_SAMPLES, &gvar); + if (ret != SR_OK) { + hardware_connect_failed(); + return; + } + + _cur_samplelimits = g_variant_get_uint64(gvar); g_variant_unref(gvar); } - // Set the sample rate of all data - const set< boost::shared_ptr > data_set = get_data(); + // Set the sample rate of all SignalData + // Logic/Analog/Dso + set< boost::shared_ptr > data_set; + BOOST_FOREACH(const boost::shared_ptr sig, _signals) { + assert(sig); + data_set.insert(sig->data()); + } BOOST_FOREACH(boost::shared_ptr data, data_set) { assert(data); - data->set_samplerate(sample_rate); + data->set_samplerate(_cur_samplerate); } +#ifdef ENABLE_DECODE + // DecoderStack + BOOST_FOREACH(const boost::shared_ptr d, _decode_traces) + { + assert(d); + d->decoder()->set_samplerate(_cur_samplerate); + } +#endif + // MathStack BOOST_FOREACH(const boost::shared_ptr m, _math_traces) { assert(m); - m->get_math_stack()->set_samplerate(sample_rate); + m->get_math_stack()->set_samplerate(_cur_samplerate); } - _group_data->set_samplerate(sample_rate); + // Group + _group_data->set_samplerate(_cur_samplerate); } void SigSession::feed_in_header(const sr_dev_inst *sdi) @@ -623,7 +667,7 @@ void SigSession::feed_in_header(const sr_dev_inst *sdi) i++) (*i)->decoder()->stop_decode(); #endif - read_sample_rate(sdi); + update_data_header(sdi); //receive_data(0); } @@ -640,8 +684,9 @@ void SigSession::add_group() if (probe_index_list.size() > 1) { //_group_data.reset(new data::Group(_last_sample_rate)); - if (_group_data->get_snapshots().empty()) - _group_data->set_samplerate(_dev_inst->get_sample_rate()); +// if (_group_data->get_snapshots().empty()) +// _group_data->set_samplerate(_dev_inst->get_sample_rate()); + _group_data->set_samplerate(_cur_samplerate); const boost::shared_ptr signal( new view::GroupSignal("New Group", _group_data, probe_index_list, _group_cnt)); @@ -811,6 +856,7 @@ void SigSession::reload() if (_capture_state == Running) stop_capture(); + //refresh(0); vector< boost::shared_ptr > sigs; boost::shared_ptr signal; @@ -869,10 +915,23 @@ void SigSession::refresh(int holdtime) if (_logic_data) { _logic_data->clear(); _cur_logic_snapshot.reset(); +#ifdef ENABLE_DECODE + BOOST_FOREACH(const boost::shared_ptr d, _decode_traces) + { + assert(d); + d->decoder()->clear(); + } +#endif } if (_dso_data) { _dso_data->clear(); _cur_dso_snapshot.reset(); + // MathStack + BOOST_FOREACH(const boost::shared_ptr m, _math_traces) + { + assert(m); + m->get_math_stack()->clear(); + } } if (_analog_data) { _analog_data->clear(); diff --git a/DSView/pv/sigsession.h b/DSView/pv/sigsession.h index 9005ab7f..55e9ec53 100644 --- a/DSView/pv/sigsession.h +++ b/DSView/pv/sigsession.h @@ -128,6 +128,10 @@ public: capture_state get_capture_state() const; + uint64_t cur_samplerate() const; + uint64_t cur_samplelimits() const; + double cur_sampletime() const; + void start_capture(bool instant, boost::function error_handler); @@ -187,7 +191,7 @@ public: private: void set_capture_state(capture_state state); - void read_sample_rate(const sr_dev_inst *const sdi); + void update_data_header(const sr_dev_inst *const sdi); private: /** @@ -237,6 +241,8 @@ private: mutable boost::mutex _sampling_mutex; capture_state _capture_state; bool _instant; + uint64_t _cur_samplerate; + uint64_t _cur_samplelimits; mutable boost::mutex _signals_mutex; std::vector< boost::shared_ptr > _signals; @@ -306,6 +312,8 @@ signals: void show_region(uint64_t start, uint64_t end); + void hardware_connect_failed(); + public slots: void reload(); void refresh(int holdtime); diff --git a/DSView/pv/storesession.cpp b/DSView/pv/storesession.cpp index 6d2ea359..14fe7cd1 100644 --- a/DSView/pv/storesession.cpp +++ b/DSView/pv/storesession.cpp @@ -25,6 +25,8 @@ #include #include +#include + using boost::dynamic_pointer_cast; using boost::mutex; using boost::shared_ptr; @@ -70,9 +72,12 @@ const QString& StoreSession::error() const bool StoreSession::start() { - set< shared_ptr > data_set = - _session.get_data(); const vector< shared_ptr > sigs(_session.get_signals()); + set< boost::shared_ptr > data_set; + BOOST_FOREACH(const boost::shared_ptr sig, sigs) { + assert(sig); + data_set.insert(sig->data()); + } // Check we have logic data if (data_set.empty() || sigs.empty()) { diff --git a/DSView/pv/toolbars/samplingbar.cpp b/DSView/pv/toolbars/samplingbar.cpp index 466e382d..05616ca3 100644 --- a/DSView/pv/toolbars/samplingbar.cpp +++ b/DSView/pv/toolbars/samplingbar.cpp @@ -126,17 +126,6 @@ SamplingBar::SamplingBar(SigSession &session, QWidget *parent) : _run_stop_button.setIcon(_icon_start); _instant_button.setIcon(_icon_instant); -// for (size_t i = 0; i < countof(RecordLengths); i++) -// { -// const uint64_t &l = RecordLengths[i]; -// char *const text = ds_si_string_u64(l, " samples"); -// _sample_count.addItem(QString(text), -// qVariantFromValue(l)); -// g_free(text); - -// if (l == DefaultRecordLength) -// _sample_count.setCurrentIndex(i); -// } _sample_count.setSizeAdjustPolicy(QComboBox::AdjustToContents); set_sampling(false); connect(&_sample_count, SIGNAL(currentIndexChanged(int)), @@ -191,7 +180,6 @@ void SamplingBar::set_device_list( update_sample_rate_selector(); update_sample_count_selector(); - update_scale(); _updating_device_selector = false; } @@ -454,8 +442,7 @@ void SamplingBar::update_sample_rate_selector_value() break; } } - if (samplerate != _sample_rate.itemData(_sample_rate.currentIndex()).value()) - update_scale(); + _updating_sample_rate = false; } @@ -486,7 +473,6 @@ void SamplingBar::commit_sample_rate() get_selected_device()->set_config(NULL, NULL, SR_CONF_SAMPLERATE, g_variant_new_uint64(sample_rate)); - update_scale(); } _updating_sample_rate = false; @@ -512,7 +498,6 @@ void SamplingBar::on_samplecount_sel(int index) g_variant_new_uint64(sample_count)); sample_count_changed(); - //update_scale(); } } @@ -535,8 +520,6 @@ void SamplingBar::on_samplerate_sel(int index) get_selected_device()->set_config(NULL, NULL, SR_CONF_SAMPLERATE, g_variant_new_uint64(sample_rate)); - - //update_scale(); } } @@ -628,7 +611,6 @@ void SamplingBar::update_sample_count_selector_value() if (samplecount != _sample_count.itemData(_sample_count.currentIndex()).value()) { sample_count_changed(); - update_scale(); } _updating_sample_count = false; } @@ -661,7 +643,6 @@ void SamplingBar::commit_sample_count() get_selected_device()->set_config(NULL, NULL, SR_CONF_LIMIT_SAMPLES, g_variant_new_uint64(sample_count)); - update_scale(); } _updating_sample_count = false; diff --git a/DSView/pv/toolbars/samplingbar.h b/DSView/pv/toolbars/samplingbar.h index ad48c179..377246e4 100644 --- a/DSView/pv/toolbars/samplingbar.h +++ b/DSView/pv/toolbars/samplingbar.h @@ -95,7 +95,6 @@ signals: void instant_stop(); void device_selected(); void device_updated(); - void update_scale(); void sample_count_changed(); private: diff --git a/DSView/pv/view/cursor.cpp b/DSView/pv/view/cursor.cpp index baa00faf..9f108e9a 100644 --- a/DSView/pv/view/cursor.cpp +++ b/DSView/pv/view/cursor.cpp @@ -60,7 +60,7 @@ Cursor::Cursor(View &view, QColor color, uint64_t index) : QRectF Cursor::get_label_rect(const QRect &rect) const { - const double samples_per_pixel = _view.session().get_device()->get_sample_rate() * _view.scale(); + const double samples_per_pixel = _view.session().cur_samplerate() * _view.scale(); const double x = _index/samples_per_pixel - (_view.offset() / _view.scale()); const QSizeF label_size( @@ -116,7 +116,7 @@ void Cursor::paint_label(QPainter &p, const QRect &rect, p.drawLine(close.left() + 2, close.bottom() - 2, close.right() - 2, close.top() + 2); p.drawText(r, Qt::AlignCenter | Qt::AlignVCenter, - Ruler::format_real_time(_index, _view.session().get_device()->get_sample_rate())); + Ruler::format_real_time(_index, _view.session().cur_samplerate())); const QRectF arrowRect = QRectF(r.bottomLeft().x(), r.bottomLeft().y(), r.width(), ArrowSize); p.drawText(arrowRect, Qt::AlignCenter | Qt::AlignVCenter, QString::number(index)); @@ -143,7 +143,7 @@ void Cursor::paint_fix_label(QPainter &p, const QRect &rect, p.setPen(Qt::white); p.drawText(r, Qt::AlignCenter | Qt::AlignVCenter, - Ruler::format_real_time(_index, _view.session().get_device()->get_sample_rate())); + Ruler::format_real_time(_index, _view.session().cur_samplerate())); const QRectF arrowRect = QRectF(r.bottomLeft().x(), r.bottomLeft().y(), r.width(), ArrowSize); p.drawText(arrowRect, Qt::AlignCenter | Qt::AlignVCenter, label); @@ -153,7 +153,7 @@ void Cursor::compute_text_size(QPainter &p, unsigned int prefix) { (void)prefix; _text_size = p.boundingRect(QRectF(), 0, - Ruler::format_real_time(_index, _view.session().get_device()->get_sample_rate())).size(); + Ruler::format_real_time(_index, _view.session().cur_samplerate())).size(); } } // namespace view diff --git a/DSView/pv/view/decodetrace.cpp b/DSView/pv/view/decodetrace.cpp index b7117769..8d9ea2d7 100644 --- a/DSView/pv/view/decodetrace.cpp +++ b/DSView/pv/view/decodetrace.cpp @@ -125,6 +125,7 @@ DecodeTrace::DecodeTrace(pv::SigSession &session, _end_index(0), _start_count(0), _end_count(0), + _progress(0), _popup_form(NULL), _popup() { @@ -175,7 +176,7 @@ void DecodeTrace::paint_back(QPainter &p, int left, int right) p.drawLine(left, sigY, right, sigY); // --draw decode region control - const double samples_per_pixel = _session.get_device()->get_sample_rate() * _view->scale(); + const double samples_per_pixel = _session.cur_samplerate() * _view->scale(); const double startX = _decode_start/samples_per_pixel - (_view->offset() / _view->scale()); const double endX = _decode_end/samples_per_pixel - (_view->offset() / _view->scale()); const double regionY = get_y() - _totalHeight*0.5 - ControlRectWidth; @@ -665,7 +666,7 @@ bool DecodeTrace::draw_unresolved_period(QPainter &p, int h, int left, font.setPointSize(_view->get_signalHeight()*2/3); font.setBold(true); p.setFont(font); - p.drawText(no_decode_rect, Qt::AlignCenter | Qt::AlignVCenter, QString::number(progress100)+"%"); + p.drawText(no_decode_rect, Qt::AlignCenter | Qt::AlignVCenter, QString::number(_progress)+"%"); return true; } @@ -829,10 +830,24 @@ void DecodeTrace::commit_probes() void DecodeTrace::on_new_decode_data() { + const uint64_t need_sample_count = _decode_end - _decode_start + 1; + if (need_sample_count == 0) { + _progress = 100; + } else { + const uint64_t samples_decoded = _decoder_stack->samples_decoded(); + _progress = ceil(samples_decoded * 100.0 / need_sample_count); + } + decoded_progress(_progress); + if (_view && _view->session().get_capture_state() == SigSession::Stopped) _view->data_updated(); } +int DecodeTrace::get_progress() const +{ + return _progress; +} + void DecodeTrace::on_decode_done() { if (_view) { @@ -937,7 +952,7 @@ QRectF DecodeTrace::get_rect(DecodeSetRegions type, int y, int right) void DecodeTrace::on_region_set(int index) { (void)index; - const uint64_t last_samples = _session.get_device()->get_sample_limit() - 1; + const uint64_t last_samples = _session.cur_samplelimits() - 1; const int index1 = _start_comboBox->currentIndex(); const int index2 = _end_comboBox->currentIndex(); uint64_t decode_start, decode_end; @@ -974,7 +989,7 @@ void DecodeTrace::on_region_set(int index) void DecodeTrace::frame_ended() { - const uint64_t last_samples = _session.get_device()->get_sample_limit() - 1; + const uint64_t last_samples = _session.cur_samplelimits() - 1; if (_decode_start > last_samples) { _decode_start = 0; _start_index = 0; diff --git a/DSView/pv/view/decodetrace.h b/DSView/pv/view/decodetrace.h index 553da4d9..41cc94fe 100644 --- a/DSView/pv/view/decodetrace.h +++ b/DSView/pv/view/decodetrace.h @@ -141,6 +141,8 @@ public: **/ void frame_ended(); + int get_progress() const; + protected: void paint_type_options(QPainter &p, int right, const QPoint pt); @@ -187,6 +189,9 @@ private: void commit_probes(); +signals: + void decoded_progress(int progress); + private slots: void on_new_decode_data(); @@ -209,6 +214,7 @@ private: int _start_index, _end_index; int _start_count, _end_count; QComboBox *_start_comboBox, *_end_comboBox; + int _progress; std::list< boost::shared_ptr > _bindings; diff --git a/DSView/pv/view/dsosignal.cpp b/DSView/pv/view/dsosignal.cpp index 558ce007..f2a4b66b 100644 --- a/DSView/pv/view/dsosignal.cpp +++ b/DSView/pv/view/dsosignal.cpp @@ -305,7 +305,7 @@ bool DsoSignal::go_hDialPre(bool setted) { int ch_num = _view->session().get_ch_num(SR_CHANNEL_DSO); if (ch_num != 0 && !_hDial->isMin()) { - uint64_t sample_rate = _view->session().get_device()->get_sample_rate(); + uint64_t sample_rate = _view->session().cur_samplerate(); const uint64_t min_div = std::pow(10.0, 9.0) / sample_rate; if (_view->session().get_capture_state() != SigSession::Running && !_data->get_snapshots().empty()) { @@ -764,8 +764,8 @@ void DsoSignal::paint_back(QPainter &p, int left, int right) p.setPen(Trace::dsLightBlue); p.drawLine(left, UpMargin/2, left + width, UpMargin/2); - const uint64_t sample_len = _dev_inst->get_sample_limit(); - const double samplerate = _dev_inst->get_sample_rate(); + const uint64_t sample_len = _view->session().cur_samplelimits(); + const double samplerate = _view->session().cur_samplerate(); const double samples_per_pixel = samplerate * _view->scale(); const double shown_rate = min(samples_per_pixel * width * 1.0 / sample_len, 1.0); const double start_time = _data->get_start_time(); @@ -843,7 +843,7 @@ void DsoSignal::paint_mid(QPainter &p, int left, int right) const double pixels_offset = offset / scale; //const double samplerate = _data->samplerate(); - const double samplerate = _dev_inst->get_sample_rate(); + const double samplerate = _view->session().cur_samplerate(); const double start_time = _data->get_start_time(); const int64_t last_sample = max((int64_t)(snapshot->get_sample_count() - 1), (int64_t)0); const double samples_per_pixel = samplerate * scale; @@ -1280,7 +1280,7 @@ void DsoSignal::paint_measure(QPainter &p) double value_p2p = value_max - value_min; _period = (count == 0) ? period * 10.0 : period * 10.0 / count; const int channel_count = _view->session().get_ch_num(SR_CHANNEL_DSO); - uint64_t sample_rate = _dev_inst->get_sample_rate(); + uint64_t sample_rate = _view->session().cur_samplerate(); _period = _period * 200.0 / (channel_count * sample_rate * 1.0 / SR_MHZ(1)); _ms_string[DSO_MS_VMAX] = "Vmax: " + (abs(value_max) > 1000 ? QString::number(value_max/1000.0, 'f', 2) + "V" : QString::number(value_max, 'f', 2) + "mV"); _ms_string[DSO_MS_VMIN] = "Vmin: " + (abs(value_min) > 1000 ? QString::number(value_min/1000.0, 'f', 2) + "V" : QString::number(value_min, 'f', 2) + "mV"); @@ -1455,7 +1455,7 @@ bool DsoSignal::measure(const QPointF &p) assert(scale > 0); const double offset = _view->offset(); const double pixels_offset = offset / scale; - const double samplerate = _dev_inst->get_sample_rate(); + const double samplerate = _view->session().cur_samplerate(); const double samples_per_pixel = samplerate * scale; _hover_index = floor((p.x() + pixels_offset) * samples_per_pixel+0.5); diff --git a/DSView/pv/view/mathtrace.cpp b/DSView/pv/view/mathtrace.cpp index d8bb16c2..459c15a9 100644 --- a/DSView/pv/view/mathtrace.cpp +++ b/DSView/pv/view/mathtrace.cpp @@ -359,8 +359,8 @@ void MathTrace::paint_fore(QPainter &p, int left, int right) double blank_right = width; // horizontal ruler - const double NyFreq = _session.get_device()->get_sample_rate() / (2.0 * _math_stack->get_sample_interval()); - const double deltaFreq = _session.get_device()->get_sample_rate() * 1.0 / + const double NyFreq = _session.cur_samplerate() / (2.0 * _math_stack->get_sample_interval()); + const double deltaFreq = _session.cur_samplerate() * 1.0 / (_math_stack->get_sample_num() * _math_stack->get_sample_interval()); const double FreqRange = NyFreq * _scale; const double FreqOffset = NyFreq * _offset; diff --git a/DSView/pv/view/ruler.cpp b/DSView/pv/view/ruler.cpp index 1bbfb340..61963fed 100644 --- a/DSView/pv/view/ruler.cpp +++ b/DSView/pv/view/ruler.cpp @@ -205,7 +205,7 @@ void Ruler::mouseMoveEvent(QMouseEvent *e) if (_grabbed_marker) { _grabbed_marker->set_index((_view.offset() + - _view.hover_point().x() * _view.scale()) * _view.session().get_device()->get_sample_rate()); + _view.hover_point().x() * _view.scale()) * _view.session().cur_samplerate()); } update(); @@ -264,7 +264,7 @@ void Ruler::mouseReleaseEvent(QMouseEvent *event) _cursor_sel_visible = true; } else { int overCursor; - uint64_t index = (_view.offset() + (_cursor_sel_x + 0.5) * _view.scale()) * _view.session().get_device()->get_sample_rate(); + uint64_t index = (_view.offset() + (_cursor_sel_x + 0.5) * _view.scale()) * _view.session().cur_samplerate(); overCursor = in_cursor_sel_rect(event->pos()); if (overCursor == 0) { _view.add_cursor(CursorColor[_view.get_cursorList().size() % 8], index); @@ -426,7 +426,7 @@ void Ruler::draw_logic_tick_mark(QPainter &p) const double MinValueSpacing = 16.0; const int ValueMargin = 5; - const double abs_min_period = 10.0 / _view.session().get_device()->get_sample_rate(); + const double abs_min_period = 10.0 / _view.session().cur_samplerate(); double min_width = SpacingIncrement; double typical_width; diff --git a/DSView/pv/view/timemarker.cpp b/DSView/pv/view/timemarker.cpp index 34a0e7d7..bf4f9c87 100644 --- a/DSView/pv/view/timemarker.cpp +++ b/DSView/pv/view/timemarker.cpp @@ -70,7 +70,7 @@ void TimeMarker::set_index(uint64_t index) void TimeMarker::paint(QPainter &p, const QRect &rect, const bool highlight) { - const double samples_per_pixel = _view.session().get_device()->get_sample_rate() * _view.scale(); + const double samples_per_pixel = _view.session().cur_samplerate() * _view.scale(); const double x = _index/samples_per_pixel - (_view.offset() / _view.scale()); p.setPen((_grabbed | highlight) ? QPen(_colour.lighter(), 2, Qt::DashLine) : QPen(_colour, 1, Qt::DashLine)); p.drawLine(QPointF(x, rect.top()), QPointF(x, rect.bottom())); diff --git a/DSView/pv/view/view.cpp b/DSView/pv/view/view.cpp index 43cf5868..d97d4759 100644 --- a/DSView/pv/view/view.cpp +++ b/DSView/pv/view/view.cpp @@ -418,14 +418,16 @@ void View::show_search_cursor(bool show) void View::set_trig_pos(quint64 trig_pos) { - const double time = trig_pos * 1.0 / _session.get_device()->get_sample_rate(); + const double time = trig_pos * 1.0 / _session.cur_samplerate(); _trig_pos = trig_pos; _trig_cursor->set_index(trig_pos); - _show_trig_cursor = true; - set_scale_offset(_scale, time - _scale * get_view_width() / 2); + if (ds_trigger_get_en()) { + _show_trig_cursor = true; + set_scale_offset(_scale, time - _scale * get_view_width() / 2); + } _trigger_time = QDateTime::currentDateTime(); - const int64_t secs = time - _session.get_device()->get_sample_time(); + const int64_t secs = time - _session.cur_sampletime(); _trigger_time = _trigger_time.addSecs(secs); _ruler->update(); @@ -436,7 +438,7 @@ void View::set_search_pos(uint64_t search_pos) { //assert(search_pos >= 0); - const double time = search_pos * 1.0 / _session.get_device()->get_sample_rate(); + const double time = search_pos * 1.0 / _session.cur_samplerate(); _search_pos = search_pos; _search_cursor->set_index(search_pos); set_scale_offset(_scale, time - _scale * get_view_width() / 2); @@ -492,7 +494,7 @@ void View::get_scroll_layout(double &length, double &offset) const if (data_set.empty()) return; - length = _session.get_device()->get_sample_time() / _scale; + length = _session.cur_sampletime() / _scale; offset = _offset / _scale; } @@ -527,23 +529,25 @@ void View::update_scroll() verticalScrollBar()->setRange(0,0); } -void View::update_scale() +void View::update_scale_offset() { - const uint64_t sample_rate = _session.get_device()->get_sample_rate(); + const uint64_t sample_rate = _session.cur_samplerate(); assert(sample_rate > 0); if (_session.get_device()->dev_inst()->mode != DSO) { - _scale = (1.0 / sample_rate) / WellPixelsPerSample; - _maxscale = _session.get_device()->get_sample_time() / (get_view_width() * MaxViewRate); + //_scale = (1.0 / sample_rate) / WellPixelsPerSample; + _maxscale = _session.cur_sampletime() / (get_view_width() * MaxViewRate); } else { _scale = _session.get_device()->get_time_base() * 10.0 / get_view_width() * std::pow(10.0, -9.0); _maxscale = 1e9; } - _minscale = (1.0 / sample_rate) / MaxPixelsPerSample; - //_offset = 0; + + _scale = max(min(_scale, _maxscale), _minscale); + _offset = max(min(_offset, get_max_offset()), get_min_offset()); + _preScale = _scale; - //_preOffset = _offset; + _preOffset = _offset; _trig_cursor->set_index(_trig_pos); @@ -722,7 +726,7 @@ void View::resizeEvent(QResizeEvent*) _scale = _session.get_device()->get_time_base() * std::pow(10.0, -9.0) * DS_CONF_DSO_HDIVS / get_view_width(); if (_session.get_device()->dev_inst()->mode != DSO) - _maxscale = _session.get_device()->get_sample_time() / (get_view_width() * MaxViewRate); + _maxscale = _session.cur_sampletime() / (get_view_width() * MaxViewRate); else _maxscale = 1e9; @@ -860,7 +864,7 @@ void View::set_cursor_middle(int index) list::iterator i = _cursorList.begin(); while (index-- != 0) i++; - set_scale_offset(_scale, (*i)->index() * 1.0 / _session.get_device()->get_sample_rate() - _scale * get_view_width() / 2); + set_scale_offset(_scale, (*i)->index() * 1.0 / _session.cur_samplerate() - _scale * get_view_width() / 2); } void View::on_measure_updated() @@ -879,7 +883,7 @@ QString View::get_measure(QString option) QString View::get_cm_time(int index) { - return _ruler->format_real_time(get_cursor_samples(index), _session.get_device()->get_sample_rate()); + return _ruler->format_real_time(get_cursor_samples(index), _session.cur_samplerate()); } QString View::get_cm_delta(int index1, int index2) @@ -890,7 +894,7 @@ QString View::get_cm_delta(int index1, int index2) 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.get_device()->get_sample_rate()); + return _ruler->format_real_time(delta_sample, _session.cur_samplerate()); } uint64_t View::get_cursor_samples(int index) @@ -924,6 +928,7 @@ void View::on_state_changed(bool stop) BOOST_FOREACH(Viewport *viewport, _viewport_list) viewport->stop_trigger_timer(); } + update_scale_offset(); } int View::get_view_width() @@ -963,7 +968,7 @@ double View::get_min_offset() double View::get_max_offset() { - return _session.get_device()->get_sample_time() + return _session.cur_sampletime() - _scale * (get_view_width() * MaxViewRate); } @@ -975,9 +980,9 @@ QString View::trigger_time() void View::show_region(uint64_t start, uint64_t end) { assert(start <= end); - const double ideal_scale = (end-start) * 2.0 / _session.get_device()->get_sample_rate() / get_view_width(); + const double ideal_scale = (end-start) * 2.0 / _session.cur_samplerate() / get_view_width(); const double new_scale = max(min(ideal_scale, _maxscale), _minscale); - const double new_off = (start + end) * 0.5 / _session.get_device()->get_sample_rate() - new_scale * get_view_width() / 2; + const double new_off = (start + end) * 0.5 / _session.cur_samplerate() - new_scale * get_view_width() / 2; set_scale_offset(new_scale, new_off); } diff --git a/DSView/pv/view/view.h b/DSView/pv/view/view.h index a90f8fb4..028eee48 100644 --- a/DSView/pv/view/view.h +++ b/DSView/pv/view/view.h @@ -229,7 +229,7 @@ public slots: void set_measure_en(int enable); void signals_changed(); void data_updated(); - void update_scale(); + void update_scale_offset(); void show_region(uint64_t start, uint64_t end); private slots: diff --git a/DSView/pv/view/viewport.cpp b/DSView/pv/view/viewport.cpp index 4ecbc294..99263a85 100644 --- a/DSView/pv/view/viewport.cpp +++ b/DSView/pv/view/viewport.cpp @@ -206,7 +206,7 @@ void Viewport::paintSignals(QPainter &p) if (_view.cursors_shown() && _type == TIME_VIEW) { list::iterator i = _view.get_cursorList().begin(); double cursorX; - const double samples_per_pixel = _view.session().get_device()->get_sample_rate() * _view.scale(); + const double samples_per_pixel = _view.session().cur_samplerate() * _view.scale(); while (i != _view.get_cursorList().end()) { cursorX = (*i)->index()/samples_per_pixel - (_view.offset() / _view.scale()); if (rect().contains(_view.hover_point().x(), _view.hover_point().y()) && @@ -242,7 +242,7 @@ void Viewport::paintProgress(QPainter &p) { using pv::view::Signal; - const uint64_t _total_sample_len = _view.session().get_device()->get_sample_limit(); + const uint64_t _total_sample_len = _view.session().cur_samplelimits(); double progress = -(_total_receive_len * 1.0 / _total_sample_len * 360 * 16); int captured_progress = 0; @@ -407,7 +407,7 @@ void Viewport::mousePressEvent(QMouseEvent *event) _action_type = LOGIC_ZOOM; } else if (_view.session().get_device()->dev_inst()->mode == DSO) { if (_hover_hit) { - uint64_t index = (_view.offset() + (event->pos().x() + 0.5) * _view.scale()) * _view.session().get_device()->get_sample_rate(); + uint64_t index = (_view.offset() + (event->pos().x() + 0.5) * _view.scale()) * _view.session().cur_samplerate(); _view.add_cursor(view::Ruler::CursorColor[_view.get_cursorList().size() % 8], index); _view.show_cursors(true); } @@ -450,7 +450,7 @@ void Viewport::mouseMoveEvent(QMouseEvent *event) } if (_action_type == CURS_MOVE) { - uint64_t sample_rate = _view.session().get_device()->get_sample_rate(); + uint64_t sample_rate = _view.session().cur_samplerate(); TimeMarker* grabbed_marker = _view.get_ruler()->get_grabbed_cursor(); if (_view.cursors_shown() && grabbed_marker) { const double cur_time = _view.offset() + _view.hover_point().x() * _view.scale(); @@ -485,7 +485,7 @@ void Viewport::mouseReleaseEvent(QMouseEvent *event) if (_action_type == NO_ACTION && _view.cursors_shown()) { list::iterator i = _view.get_cursorList().begin(); double cursorX; - const double samples_per_pixel = _view.session().get_device()->get_sample_rate() * _view.scale(); + const double samples_per_pixel = _view.session().cur_samplerate() * _view.scale(); while (i != _view.get_cursorList().end()) { cursorX = (*i)->index()/samples_per_pixel - (_view.offset() / _view.scale()); if ((*i)->grabbed()) { @@ -526,7 +526,7 @@ void Viewport::mouseReleaseEvent(QMouseEvent *event) assert(s); if (abs(event->pos().y() - s->get_y()) < _view.get_signalHeight()) { _action_type = LOGIC_EDGE; - _edge_start = (_view.offset() + (event->pos().x() + 0.5) * _view.scale()) * _view.session().get_device()->get_sample_rate(); + _edge_start = (_view.offset() + (event->pos().x() + 0.5) * _view.scale()) * _view.session().cur_samplerate(); break; } } @@ -581,7 +581,7 @@ void Viewport::mouseReleaseEvent(QMouseEvent *event) } } else if (_action_type == DSO_XM_STEP1) { if (event->button() == Qt::LeftButton) { - const uint64_t sample_rate = _view.session().get_device()->get_sample_rate(); + const uint64_t sample_rate = _view.session().cur_samplerate(); const double scale = _view.scale(); const double samples_per_pixel = sample_rate * scale; @@ -602,7 +602,7 @@ void Viewport::mouseReleaseEvent(QMouseEvent *event) } } else if (_action_type == DSO_XM_STEP2) { if (event->button() == Qt::LeftButton) { - const uint64_t sample_rate = _view.session().get_device()->get_sample_rate(); + const uint64_t sample_rate = _view.session().cur_samplerate(); const double scale = _view.scale(); const double samples_per_pixel = sample_rate * scale; _dso_xm_index[2] = event->pos().x() * samples_per_pixel + _view.offset() * sample_rate; @@ -666,7 +666,7 @@ void Viewport::mouseDoubleClickEvent(QMouseEvent *event) else _view.set_scale_offset(_view.get_maxscale(), 0); } else if (event->button() == Qt::LeftButton) { - uint64_t index = (_view.offset() + (event->pos().x() + 0.5) * _view.scale()) * _view.session().get_device()->get_sample_rate(); + uint64_t index = (_view.offset() + (event->pos().x() + 0.5) * _view.scale()) * _view.session().cur_samplerate(); _view.add_cursor(view::Ruler::CursorColor[_view.get_cursorList().size() % 8], index); _view.show_cursors(true); } @@ -683,7 +683,7 @@ void Viewport::mouseDoubleClickEvent(QMouseEvent *event) _mm_duty = "#####"; measure_updated(); } else if (_action_type == NO_ACTION) { - uint64_t sample_rate = _view.session().get_device()->get_sample_rate(); + uint64_t sample_rate = _view.session().cur_samplerate(); double scale = _view.scale(); const double samples_per_pixel = sample_rate * scale; _dso_xm_index[0] = event->pos().x() * samples_per_pixel + @@ -774,8 +774,8 @@ void Viewport::set_receive_len(quint64 length) start_trigger_timer(333); } else { stop_trigger_timer(); - if (_total_receive_len + length > _view.session().get_device()->get_sample_limit()) - _total_receive_len = _view.session().get_device()->get_sample_limit(); + if (_total_receive_len + length > _view.session().cur_samplelimits()) + _total_receive_len = _view.session().cur_samplelimits(); else _total_receive_len += length; } @@ -792,7 +792,7 @@ void Viewport::measure() { _measure_type = NO_MEASURE; if (_type == TIME_VIEW) { - const uint64_t sample_rate = _view.session().get_device()->get_sample_rate(); + const uint64_t sample_rate = _view.session().cur_samplerate(); const vector< boost::shared_ptr > sigs(_view.session().get_signals()); BOOST_FOREACH(const boost::shared_ptr s, sigs) { assert(s); @@ -988,7 +988,7 @@ void Viewport::paintMeasure(QPainter &p) p.setPen(QPen(dsoSig->get_colour(), 1, Qt::DotLine)); const int text_height = p.boundingRect(0, 0, INT_MAX, INT_MAX, Qt::AlignLeft | Qt::AlignTop, "W").height(); - const uint64_t sample_rate = _view.session().get_device()->get_sample_rate(); + const uint64_t sample_rate = _view.session().cur_samplerate(); const double x = (_dso_ym_index / (sample_rate * _view.scale())) - _view.offset() /_view.scale(); p.drawLine(x-10, _dso_ym_start, @@ -1034,7 +1034,7 @@ void Viewport::paintMeasure(QPainter &p) int measure_line_count = 6; const int text_height = p.boundingRect(0, 0, INT_MAX, INT_MAX, Qt::AlignLeft | Qt::AlignTop, "W").height(); - const uint64_t sample_rate = _view.session().get_device()->get_sample_rate(); + const uint64_t sample_rate = _view.session().cur_samplerate(); QLineF *line; QLineF *const measure_lines = new QLineF[measure_line_count]; line = measure_lines; diff --git a/libsigrok4DSL/hardware/DSL/dslogic.c b/libsigrok4DSL/hardware/DSL/dslogic.c index 2250da53..bed14429 100644 --- a/libsigrok4DSL/hardware/DSL/dslogic.c +++ b/libsigrok4DSL/hardware/DSL/dslogic.c @@ -393,8 +393,8 @@ static int fpga_setting(const struct sr_dev_inst *sdi) setting.trig_logic1[0] = (trigger->trigger_logic[TriggerStages] << 1) + trigger->trigger1_inv[TriggerStages]; for (i = 1; i < NUM_TRIGGER_STAGES; i++) { - setting.trig_mask0[i] = 0xff; - setting.trig_mask1[i] = 0xff; + setting.trig_mask0[i] = 0xffff; + setting.trig_mask1[i] = 0xffff; setting.trig_value0[i] = 0; setting.trig_value1[i] = 0; @@ -1100,34 +1100,32 @@ static int dev_open(struct sr_dev_inst *sdi) return SR_ERR; } - if (devc->fw_updated > 0) { - if ((ret = command_fpga_config(usb->devhdl)) != SR_OK) { - sr_err("Send FPGA configure command failed!"); - } else { - /* Takes >= 10ms for the FX2 to be ready for FPGA configure. */ - g_usleep(10 * 1000); - char *fpga_bit; - if (!(fpga_bit = g_try_malloc(strlen(config_path)+strlen(devc->profile->fpga_bit33)+1))) { - sr_err("fpag_bit path malloc error!"); - return SR_ERR_MALLOC; - } - strcpy(fpga_bit, config_path); - switch(devc->th_level) { - case SR_TH_3V3: - strcat(fpga_bit, devc->profile->fpga_bit33);; - break; - case SR_TH_5V0: - strcat(fpga_bit, devc->profile->fpga_bit50);; - break; - default: - return SR_ERR; - } - ret = fpga_config(usb->devhdl, fpga_bit); - if (ret != SR_OK) { - sr_err("Configure FPGA failed!"); - } - g_free(fpga_bit); + if ((ret = command_fpga_config(usb->devhdl)) != SR_OK) { + sr_err("Send FPGA configure command failed!"); + } else { + /* Takes >= 10ms for the FX2 to be ready for FPGA configure. */ + g_usleep(10 * 1000); + char *fpga_bit; + if (!(fpga_bit = g_try_malloc(strlen(config_path)+strlen(devc->profile->fpga_bit33)+1))) { + sr_err("fpag_bit path malloc error!"); + return SR_ERR_MALLOC; } + strcpy(fpga_bit, config_path); + switch(devc->th_level) { + case SR_TH_3V3: + strcat(fpga_bit, devc->profile->fpga_bit33);; + break; + case SR_TH_5V0: + strcat(fpga_bit, devc->profile->fpga_bit50);; + break; + default: + return SR_ERR; + } + ret = fpga_config(usb->devhdl, fpga_bit); + if (ret != SR_OK) { + sr_err("Configure FPGA failed!"); + } + g_free(fpga_bit); } ret = command_vth(usb->devhdl, devc->vth); diff --git a/libsigrok4DSL/proto.h b/libsigrok4DSL/proto.h index fe9b1d95..854f445e 100644 --- a/libsigrok4DSL/proto.h +++ b/libsigrok4DSL/proto.h @@ -181,6 +181,7 @@ SR_API int ds_trigger_set_stage(uint16_t stages); SR_API int ds_trigger_set_pos(uint16_t position); SR_API uint16_t ds_trigger_get_pos(); SR_API int ds_trigger_set_en(uint16_t enable); +SR_API uint16_t ds_trigger_get_en(); SR_API int ds_trigger_set_mode(uint16_t mode); #endif diff --git a/libsigrok4DSL/trigger.c b/libsigrok4DSL/trigger.c index c3350c95..6baf5d7e 100644 --- a/libsigrok4DSL/trigger.c +++ b/libsigrok4DSL/trigger.c @@ -207,6 +207,16 @@ SR_API int ds_trigger_set_en(uint16_t enable) return SR_OK; } +/** + * get trigger en + * + * @return SR_OK upon success. + */ +SR_API uint16_t ds_trigger_get_en() +{ + return trigger->trigger_en; +} + /** * set trigger mode * From f6245e2eae6c54c2563276e6ef69ca9af5491820 Mon Sep 17 00:00:00 2001 From: DreamSourceLab Date: Mon, 16 May 2016 21:58:09 +0800 Subject: [PATCH 16/32] Improve memory alloc and free for capture session --- DSView/darkstyle/style.qss | 4 +- DSView/pv/data/analog.cpp | 6 +- DSView/pv/data/analogsnapshot.cpp | 31 +++++-- DSView/pv/data/analogsnapshot.h | 6 +- DSView/pv/data/decoderstack.cpp | 35 ++++---- DSView/pv/data/dso.cpp | 6 +- DSView/pv/data/dsosnapshot.cpp | 41 ++++++--- DSView/pv/data/dsosnapshot.h | 6 +- DSView/pv/data/groupsnapshot.h | 2 +- DSView/pv/data/logic.cpp | 6 +- DSView/pv/data/logicsnapshot.cpp | 42 ++++++--- DSView/pv/data/logicsnapshot.h | 6 +- DSView/pv/data/mathstack.cpp | 6 +- DSView/pv/data/snapshot.cpp | 69 ++++++++++----- DSView/pv/data/snapshot.h | 12 ++- DSView/pv/dock/protocoldock.cpp | 11 +-- DSView/pv/dock/protocoldock.h | 2 - DSView/pv/sigsession.cpp | 137 +++++++++++++----------------- DSView/pv/sigsession.h | 4 +- DSView/pv/toolbars/filebar.cpp | 4 +- DSView/pv/view/analogsignal.cpp | 2 + DSView/pv/view/decodetrace.cpp | 56 ++---------- DSView/pv/view/decodetrace.h | 3 - DSView/pv/view/dsosignal.cpp | 16 ++-- DSView/pv/view/logicsignal.cpp | 17 ++-- 25 files changed, 284 insertions(+), 246 deletions(-) diff --git a/DSView/darkstyle/style.qss b/DSView/darkstyle/style.qss index 9cca2c81..cacf5e26 100755 --- a/DSView/darkstyle/style.qss +++ b/DSView/darkstyle/style.qss @@ -369,7 +369,7 @@ QScrollBar:horizontal QScrollBar::handle:horizontal { background-color: #605F5F; - min-width: 5px; + min-width: 15px; border-radius: 4px; } @@ -435,7 +435,7 @@ QScrollBar:vertical QScrollBar::handle:vertical { background-color: #605F5F; - min-height: 5px; + min-height: 15px; border-radius: 4px; } diff --git a/DSView/pv/data/analog.cpp b/DSView/pv/data/analog.cpp index ad0c052f..e5c85b39 100644 --- a/DSView/pv/data/analog.cpp +++ b/DSView/pv/data/analog.cpp @@ -24,6 +24,8 @@ #include "analog.h" #include "analogsnapshot.h" +#include + using namespace boost; using namespace std; @@ -47,7 +49,9 @@ deque< boost::shared_ptr >& Analog::get_snapshots() void Analog::clear() { - _snapshots.clear(); + //_snapshots.clear(); + BOOST_FOREACH(const boost::shared_ptr s, _snapshots) + s->clear(); } } // namespace data diff --git a/DSView/pv/data/analogsnapshot.cpp b/DSView/pv/data/analogsnapshot.cpp index 085cf20c..66bf5833 100644 --- a/DSView/pv/data/analogsnapshot.cpp +++ b/DSView/pv/data/analogsnapshot.cpp @@ -46,13 +46,10 @@ const float AnalogSnapshot::LogEnvelopeScaleFactor = logf(EnvelopeScaleFactor); const uint64_t AnalogSnapshot::EnvelopeDataUnit = 64*1024; // bytes -AnalogSnapshot::AnalogSnapshot(const sr_datafeed_analog &analog, uint64_t _total_sample_len, unsigned int channel_num) : - Snapshot(sizeof(uint16_t)*channel_num, _total_sample_len, channel_num) +AnalogSnapshot::AnalogSnapshot() : + Snapshot(sizeof(uint16_t), 1, 1) { - boost::lock_guard lock(_mutex); memset(_envelope_levels, 0, sizeof(_envelope_levels)); - init(_total_sample_len * channel_num); - append_payload(analog); } AnalogSnapshot::~AnalogSnapshot() @@ -62,6 +59,24 @@ AnalogSnapshot::~AnalogSnapshot() free(e.samples); } +void AnalogSnapshot::clear() +{ + _sample_count = 0; + _ring_sample_count = 0; +} + +void AnalogSnapshot::first_payload(const sr_datafeed_analog &analog, uint64_t total_sample_count, unsigned int channel_num) +{ + _total_sample_count = total_sample_count; + _channel_num = channel_num; + _unit_size = sizeof(uint16_t)*channel_num; + boost::lock_guard lock(_mutex); + if (init(_total_sample_count*_channel_num) == SR_OK) { + append_payload(analog); + _last_ended = false; + } +} + void AnalogSnapshot::append_payload( const sr_datafeed_analog &analog) { @@ -89,7 +104,7 @@ const uint16_t* AnalogSnapshot::get_samples( // memcpy(data, (uint16_t*)_data + start_sample, sizeof(uint16_t) * // (end_sample - start_sample)); // return data; - return (uint16_t*)_data + start_sample * _channel_num; + return (uint16_t*)_data.data() + start_sample * _channel_num; } void AnalogSnapshot::get_envelope_section(EnvelopeSection &s, @@ -154,7 +169,7 @@ void AnalogSnapshot::append_payload_to_envelope_levels() dest_ptr = e0.samples + prev_length; // Iterate through the samples to populate the first level mipmap - const uint16_t *const stop_src_ptr = (uint16_t*)_data + + const uint16_t *const stop_src_ptr = (uint16_t*)_data.data() + e0.length * EnvelopeScaleFactor * _channel_num; // for (const uint16_t *src_ptr = (uint16_t*)_data + // prev_length * EnvelopeScaleFactor; @@ -167,7 +182,7 @@ void AnalogSnapshot::append_payload_to_envelope_levels() // *dest_ptr++ = sub_sample; // } - for (const uint16_t *src_ptr = (uint16_t*)_data + + for (const uint16_t *src_ptr = (uint16_t*)_data.data() + prev_length * EnvelopeScaleFactor * _channel_num + i; src_ptr < stop_src_ptr; src_ptr += EnvelopeScaleFactor * _channel_num) { diff --git a/DSView/pv/data/analogsnapshot.h b/DSView/pv/data/analogsnapshot.h index 3282f257..7df1e111 100644 --- a/DSView/pv/data/analogsnapshot.h +++ b/DSView/pv/data/analogsnapshot.h @@ -69,10 +69,14 @@ private: static const uint64_t EnvelopeDataUnit; public: - AnalogSnapshot(const sr_datafeed_analog &analog, uint64_t _total_sample_len, unsigned int channel_num); + AnalogSnapshot(); virtual ~AnalogSnapshot(); + void clear(); + + void first_payload(const sr_datafeed_analog &analog, uint64_t total_sample_count, unsigned int channel_num); + void append_payload(const sr_datafeed_analog &analog); const uint16_t* get_samples(int64_t start_sample, diff --git a/DSView/pv/data/decoderstack.cpp b/DSView/pv/data/decoderstack.cpp index e9ac125d..ef4c0589 100644 --- a/DSView/pv/data/decoderstack.cpp +++ b/DSView/pv/data/decoderstack.cpp @@ -56,8 +56,8 @@ namespace data { const double DecoderStack::DecodeMargin = 1.0; const double DecoderStack::DecodeThreshold = 0.2; -const int64_t DecoderStack::DecodeChunkLength = 1024 * 1024; -const unsigned int DecoderStack::DecodeNotifyPeriod = 1; +const int64_t DecoderStack::DecodeChunkLength = 4 * 1024; +const unsigned int DecoderStack::DecodeNotifyPeriod = 1024; mutex DecoderStack::_global_decode_mutex; @@ -332,7 +332,7 @@ void DecoderStack::clear() _sample_count = 0; _frame_complete = false; _samples_decoded = 0; - //new_decode_data(); + new_decode_data(); _error_message = QString(); for (map::const_iterator i = _rows.begin(); i != _rows.end(); i++) @@ -342,7 +342,7 @@ void DecoderStack::clear() void DecoderStack::stop_decode() { - _snapshot.reset(); + //_snapshot.reset(); if(_decode_state == Stopped) { clear(); @@ -397,12 +397,14 @@ void DecoderStack::begin_decode() if (snapshots.empty()) return; _snapshot = snapshots.front(); + if (_snapshot->empty()) + return; // Get the samplerate and start time _start_time = data->get_start_time(); _samplerate = data->samplerate(); - if (_samplerate == 0.0) - _samplerate = 1.0; + if (_samplerate == 0.0) + return; //_decode_thread = boost::thread(&DecoderStack::decode_proc, this); _decode_thread.reset(new boost::thread(&DecoderStack::decode_proc, this)); @@ -437,7 +439,8 @@ void DecoderStack::decode_data( const unsigned int unit_size, srd_session *const session) { uint8_t *chunk = NULL; - + uint64_t last_cnt = 0; + uint64_t notify_cnt = (decode_end - decode_start + 1)/100; const uint64_t chunk_sample_count = DecodeChunkLength / _snapshot->unit_size(); @@ -463,8 +466,10 @@ void DecoderStack::decode_data( _samples_decoded = chunk_end - decode_start + 1; } - if (i % DecodeNotifyPeriod == 0) + if ((i - last_cnt) > notify_cnt) { + last_cnt = i; new_decode_data(); + } } _options_changed = false; @@ -492,6 +497,12 @@ void DecoderStack::decode_proc() // Create the decoders const unsigned int unit_size = _snapshot->unit_size(); + // Get the intial sample count + { + unique_lock input_lock(_input_mutex); + sample_count = _sample_count = _snapshot->get_sample_count(); + } + BOOST_FOREACH(const shared_ptr &dec, _stack) { srd_decoder_inst *const di = dec->create_decoder_inst(session, unit_size); @@ -508,13 +519,7 @@ void DecoderStack::decode_proc() prev_di = di; decode_start = dec->decode_start(); - decode_end = min(dec->decode_end(), _snapshot->get_sample_count()); - } - - // Get the intial sample count - { - unique_lock input_lock(_input_mutex); - sample_count = _sample_count = _snapshot->get_sample_count(); + decode_end = min(dec->decode_end(), _sample_count-1); } // Start the session diff --git a/DSView/pv/data/dso.cpp b/DSView/pv/data/dso.cpp index 43663737..fa4cf4a5 100644 --- a/DSView/pv/data/dso.cpp +++ b/DSView/pv/data/dso.cpp @@ -23,6 +23,8 @@ #include "dso.h" #include "dsosnapshot.h" +#include + using namespace boost; using namespace std; @@ -46,7 +48,9 @@ deque< boost::shared_ptr >& Dso::get_snapshots() void Dso::clear() { - _snapshots.clear(); + //_snapshots.clear(); + BOOST_FOREACH(const boost::shared_ptr s, _snapshots) + s->clear(); } } // namespace data diff --git a/DSView/pv/data/dsosnapshot.cpp b/DSView/pv/data/dsosnapshot.cpp index 4e0e1b26..7fdd78f8 100644 --- a/DSView/pv/data/dsosnapshot.cpp +++ b/DSView/pv/data/dsosnapshot.cpp @@ -47,16 +47,13 @@ const uint64_t DsoSnapshot::EnvelopeDataUnit = 4*1024; // bytes const int DsoSnapshot::VrmsScaleFactor = 1 << 8; -DsoSnapshot::DsoSnapshot(const sr_datafeed_dso &dso, uint64_t _total_sample_len, unsigned int channel_num, bool instant) : - Snapshot(sizeof(uint16_t), _total_sample_len, channel_num), +DsoSnapshot::DsoSnapshot() : + Snapshot(sizeof(uint16_t), 1, 1), _envelope_en(false), _envelope_done(false), - _instant(instant) + _instant(false) { - boost::lock_guard lock(_mutex); memset(_envelope_levels, 0, sizeof(_envelope_levels)); - init(_total_sample_len); - append_payload(dso); } DsoSnapshot::~DsoSnapshot() @@ -66,6 +63,24 @@ DsoSnapshot::~DsoSnapshot() free(e.samples); } +void DsoSnapshot::clear() +{ + _sample_count = 0; + _ring_sample_count = 0; +} + +void DsoSnapshot::first_payload(const sr_datafeed_dso &dso, uint64_t total_sample_count, unsigned int channel_num, bool instant) +{ + _total_sample_count = total_sample_count; + _channel_num = channel_num; + _instant = instant; + boost::lock_guard lock(_mutex); + if (init(_total_sample_count) == SR_OK) { + append_payload(dso); + _last_ended = false; + } +} + void DsoSnapshot::append_payload(const sr_datafeed_dso &dso) { boost::lock_guard lock(_mutex); @@ -103,7 +118,7 @@ const uint8_t *DsoSnapshot::get_samples( // memcpy(data, (uint16_t*)_data + start_sample, sizeof(uint16_t) * // (end_sample - start_sample)); // return data; - return (uint8_t*)_data + start_sample * _channel_num + index * (_channel_num != 1); + return (uint8_t*)_data.data() + start_sample * _channel_num + index * (_channel_num != 1); } void DsoSnapshot::get_envelope_section(EnvelopeSection &s, @@ -170,9 +185,9 @@ void DsoSnapshot::append_payload_to_envelope_levels(bool header) dest_ptr = e0.samples + prev_length; // Iterate through the samples to populate the first level mipmap - const uint8_t *const stop_src_ptr = (uint8_t*)_data + + const uint8_t *const stop_src_ptr = (uint8_t*)_data.data() + e0.length * EnvelopeScaleFactor * _channel_num; - for (const uint8_t *src_ptr = (uint8_t*)_data + + for (const uint8_t *src_ptr = (uint8_t*)_data.data() + prev_length * EnvelopeScaleFactor * _channel_num + i; src_ptr < stop_src_ptr; src_ptr += EnvelopeScaleFactor * _channel_num) { @@ -248,9 +263,9 @@ double DsoSnapshot::cal_vrms(double zero_off, int index) const double tmp; // Iterate through the samples to populate the first level mipmap - const uint8_t *const stop_src_ptr = (uint8_t*)_data + + const uint8_t *const stop_src_ptr = (uint8_t*)_data.data() + _sample_count * _channel_num; - for (const uint8_t *src_ptr = (uint8_t*)_data + index; + for (const uint8_t *src_ptr = (uint8_t*)_data.data() + index; src_ptr < stop_src_ptr; src_ptr += VrmsScaleFactor * _channel_num) { const uint8_t * begin_src_ptr = @@ -282,9 +297,9 @@ double DsoSnapshot::cal_vmean(int index) const double vmean = 0; // Iterate through the samples to populate the first level mipmap - const uint8_t *const stop_src_ptr = (uint8_t*)_data + + const uint8_t *const stop_src_ptr = (uint8_t*)_data.data() + _sample_count * _channel_num; - for (const uint8_t *src_ptr = (uint8_t*)_data + index; + for (const uint8_t *src_ptr = (uint8_t*)_data.data() + index; src_ptr < stop_src_ptr; src_ptr += VrmsScaleFactor * _channel_num) { const uint8_t * begin_src_ptr = diff --git a/DSView/pv/data/dsosnapshot.h b/DSView/pv/data/dsosnapshot.h index eb3788bc..fbbdf849 100644 --- a/DSView/pv/data/dsosnapshot.h +++ b/DSView/pv/data/dsosnapshot.h @@ -70,10 +70,14 @@ private: static const int VrmsScaleFactor; public: - DsoSnapshot(const sr_datafeed_dso &dso, uint64_t _total_sample_len, unsigned int channel_num, bool instant); + DsoSnapshot(); virtual ~DsoSnapshot(); + void clear(); + + void first_payload(const sr_datafeed_dso &dso, uint64_t total_sample_count, unsigned int channel_num, bool instant); + void append_payload(const sr_datafeed_dso &dso); const uint8_t* get_samples(int64_t start_sample, diff --git a/DSView/pv/data/groupsnapshot.h b/DSView/pv/data/groupsnapshot.h index 729b57a7..ed475b3a 100644 --- a/DSView/pv/data/groupsnapshot.h +++ b/DSView/pv/data/groupsnapshot.h @@ -98,7 +98,7 @@ private: private: struct Envelope _envelope_levels[ScaleStepCount]; mutable boost::recursive_mutex _mutex; - void *_data; + const void *_data; uint64_t _sample_count; int _unit_size; boost::shared_ptr _signal; diff --git a/DSView/pv/data/logic.cpp b/DSView/pv/data/logic.cpp index 4116942a..a249eda8 100644 --- a/DSView/pv/data/logic.cpp +++ b/DSView/pv/data/logic.cpp @@ -24,6 +24,8 @@ #include "logic.h" #include "logicsnapshot.h" +#include + using namespace boost; using namespace std; @@ -48,7 +50,9 @@ deque< boost::shared_ptr >& Logic::get_snapshots() void Logic::clear() { - _snapshots.clear(); + //_snapshots.clear(); + BOOST_FOREACH(const boost::shared_ptr s, _snapshots) + s->clear(); } } // namespace data diff --git a/DSView/pv/data/logicsnapshot.cpp b/DSView/pv/data/logicsnapshot.cpp index ebdb904e..df77821f 100644 --- a/DSView/pv/data/logicsnapshot.cpp +++ b/DSView/pv/data/logicsnapshot.cpp @@ -43,21 +43,37 @@ const int LogicSnapshot::MipMapScaleFactor = 1 << MipMapScalePower; const float LogicSnapshot::LogMipMapScaleFactor = logf(MipMapScaleFactor); const uint64_t LogicSnapshot::MipMapDataUnit = 64*1024; // bytes -LogicSnapshot::LogicSnapshot(const sr_datafeed_logic &logic, uint64_t _total_sample_len, unsigned int channel_num) : - Snapshot(logic.unitsize, _total_sample_len, channel_num), - _last_append_sample(0) +LogicSnapshot::LogicSnapshot() : + Snapshot(1, 1, 1), + _last_append_sample(0) { - boost::lock_guard lock(_mutex); - memset(_mip_map, 0, sizeof(_mip_map)); - if (init(_total_sample_len * channel_num) == SR_OK) - append_payload(logic); + memset(_mip_map, 0, sizeof(_mip_map)); } LogicSnapshot::~LogicSnapshot() { boost::lock_guard lock(_mutex); - BOOST_FOREACH(MipMapLevel &l, _mip_map) - free(l.data); + BOOST_FOREACH(MipMapLevel &l, _mip_map) + free(l.data); +} + +void LogicSnapshot::clear() +{ + _sample_count = 0; + _ring_sample_count = 0; + memset(_mip_map, 0, sizeof(_mip_map)); +} + +void LogicSnapshot::first_payload(const sr_datafeed_logic &logic, uint64_t total_sample_count, unsigned int channel_num) +{ + _total_sample_count = total_sample_count; + _channel_num = channel_num; + _unit_size = logic.unitsize; + boost::lock_guard lock(_mutex); + if (init(_total_sample_count * _channel_num) == SR_OK) { + append_payload(logic); + _last_ended = false; + } } void LogicSnapshot::append_payload( @@ -88,7 +104,7 @@ uint8_t * LogicSnapshot::get_samples(int64_t start_sample, int64_t end_sample) c //const size_t size = (end_sample - start_sample) * _unit_size; //memcpy(data, (const uint8_t*)_data + start_sample * _unit_size, size); - return (uint8_t*)_data + start_sample * _unit_size; + return (uint8_t*)_data.data() + start_sample * _unit_size; } void LogicSnapshot::reallocate_mipmap_level(MipMapLevel &m) @@ -127,9 +143,9 @@ void LogicSnapshot::append_payload_to_mipmap() dest_ptr = (uint8_t*)m0.data + prev_length * _unit_size; // Iterate through the samples to populate the first level mipmap - const uint8_t *const end_src_ptr = (uint8_t*)_data + + const uint8_t *const end_src_ptr = (uint8_t*)_data.data() + m0.length * _unit_size * MipMapScaleFactor; - for (src_ptr = (uint8_t*)_data + + for (src_ptr = (uint8_t*)_data.data() + prev_length * _unit_size * MipMapScaleFactor; src_ptr < end_src_ptr;) { @@ -201,7 +217,7 @@ void LogicSnapshot::get_subsampled_edges( assert(sig_index >= 0); assert(sig_index < 64); - if (!_data) + if (_data.size() == 0) return; boost::lock_guard lock(_mutex); diff --git a/DSView/pv/data/logicsnapshot.h b/DSView/pv/data/logicsnapshot.h index ba40df84..ecaab06b 100644 --- a/DSView/pv/data/logicsnapshot.h +++ b/DSView/pv/data/logicsnapshot.h @@ -61,10 +61,14 @@ public: typedef std::pair EdgePair; public: - LogicSnapshot(const sr_datafeed_logic &logic, uint64_t _total_sample_len, unsigned int channel_num); + LogicSnapshot(); virtual ~LogicSnapshot(); + void clear(); + + void first_payload(const sr_datafeed_logic &logic, uint64_t total_sample_count, unsigned int channel_num); + void append_payload(const sr_datafeed_logic &logic); uint8_t * get_samples(int64_t start_sample, int64_t end_sample) const; diff --git a/DSView/pv/data/mathstack.cpp b/DSView/pv/data/mathstack.cpp index 99e3e0b4..6494351c 100644 --- a/DSView/pv/data/mathstack.cpp +++ b/DSView/pv/data/mathstack.cpp @@ -56,7 +56,8 @@ MathStack::MathStack(pv::SigSession &session, int index) : _index(index), _dc_ignore(true), _sample_interval(1), - _math_state(Init) + _math_state(Init), + _fft_plan(NULL) { } @@ -65,7 +66,8 @@ MathStack::~MathStack() _xn.clear(); _xk.clear(); _power_spectrum.clear(); - fftw_destroy_plan(_fft_plan); + if (_fft_plan) + fftw_destroy_plan(_fft_plan); } void MathStack::clear() diff --git a/DSView/pv/data/snapshot.cpp b/DSView/pv/data/snapshot.cpp index 57be15e7..e5e9e741 100644 --- a/DSView/pv/data/snapshot.cpp +++ b/DSView/pv/data/snapshot.cpp @@ -33,12 +33,13 @@ namespace pv { namespace data { Snapshot::Snapshot(int unit_size, uint64_t total_sample_count, unsigned int channel_num) : - _data(NULL), _channel_num(channel_num), _sample_count(0), _total_sample_count(total_sample_count), _ring_sample_count(0), - _unit_size(unit_size) + _unit_size(unit_size), + _memory_failed(true), + _last_ended(true) { boost::lock_guard lock(_mutex); assert(_unit_size > 0); @@ -47,41 +48,65 @@ Snapshot::Snapshot(int unit_size, uint64_t total_sample_count, unsigned int chan Snapshot::~Snapshot() { boost::lock_guard lock(_mutex); - if (_data != NULL) - free(_data); - _data = NULL; + _data.clear(); } int Snapshot::init(uint64_t _total_sample_len) { - boost::lock_guard lock(_mutex); - _data = malloc(_total_sample_len * _unit_size + - sizeof(uint64_t)); +// boost::lock_guard lock(_mutex); +// _data = malloc(_total_sample_len * _unit_size + +// sizeof(uint64_t)); - if (_data == NULL) +// if (_data == NULL) +// return SR_ERR_MALLOC; +// else +// return SR_OK; + + boost::lock_guard lock(_mutex); + uint64_t size = _total_sample_len * _unit_size + sizeof(uint64_t); + try{ + _data.resize(size); + } catch(...) { + _memory_failed = true; return SR_ERR_MALLOC; - else - return SR_OK; + } + _memory_failed = false; + return SR_OK; } -bool Snapshot::buf_null() const +bool Snapshot::memory_failed() const { - if (_data == NULL) + return _memory_failed; +} + +bool Snapshot::empty() const +{ + if (_sample_count == 0 || _memory_failed) return true; else return false; } +bool Snapshot::last_ended() const +{ + return _last_ended; +} + +void Snapshot::set_last_ended(bool ended) +{ + _last_ended = ended; +} + uint64_t Snapshot::get_sample_count() const { boost::lock_guard lock(_mutex); return _sample_count; } -void* Snapshot::get_data() const +const void* Snapshot::get_data() const { boost::lock_guard lock(_mutex); - return _data; + return _data.data(); } int Snapshot::unit_size() const @@ -100,10 +125,10 @@ uint64_t Snapshot::get_sample(uint64_t index) const { boost::lock_guard lock(_mutex); - assert(_data); + assert(_data.data()); assert(index < _sample_count); - return *(uint64_t*)((uint8_t*)_data + index * _unit_size); + return *(uint64_t*)((uint8_t*)_data.data() + index * _unit_size); } void Snapshot::append_data(void *data, uint64_t samples) @@ -117,13 +142,13 @@ void Snapshot::append_data(void *data, uint64_t samples) _sample_count = _total_sample_count; if (_ring_sample_count + samples > _total_sample_count) { - memcpy((uint8_t*)_data + _ring_sample_count * _unit_size, + memcpy((uint8_t*)_data.data() + _ring_sample_count * _unit_size, data, (_total_sample_count - _ring_sample_count) * _unit_size); _ring_sample_count = (samples + _ring_sample_count - _total_sample_count) % _total_sample_count; - memcpy((uint8_t*)_data, + memcpy((uint8_t*)_data.data(), data, _ring_sample_count * _unit_size); } else { - memcpy((uint8_t*)_data + _ring_sample_count * _unit_size, + memcpy((uint8_t*)_data.data() + _ring_sample_count * _unit_size, data, samples * _unit_size); _ring_sample_count += samples; } @@ -134,10 +159,10 @@ void Snapshot::refill_data(void *data, uint64_t samples, bool instant) boost::lock_guard lock(_mutex); if (instant) { - memcpy((uint8_t*)_data + _sample_count * _channel_num, data, samples*_channel_num); + memcpy((uint8_t*)_data.data() + _sample_count * _channel_num, data, samples*_channel_num); _sample_count = (_sample_count + samples) % (_total_sample_count + 1); } else { - memcpy((uint8_t*)_data, data, samples*_channel_num); + memcpy((uint8_t*)_data.data(), data, samples*_channel_num); _sample_count = samples; } diff --git a/DSView/pv/data/snapshot.h b/DSView/pv/data/snapshot.h index b128a83c..9f2be22c 100644 --- a/DSView/pv/data/snapshot.h +++ b/DSView/pv/data/snapshot.h @@ -42,11 +42,15 @@ public: uint64_t get_sample_count() const; - void * get_data() const; + const void * get_data() const; int unit_size() const; - bool buf_null() const; + bool memory_failed() const; + bool empty() const; + + bool last_ended() const; + void set_last_ended(bool ended); unsigned int get_channel_num() const; @@ -58,12 +62,14 @@ protected: protected: mutable boost::recursive_mutex _mutex; - void *_data; + std::vector _data; unsigned int _channel_num; uint64_t _sample_count; uint64_t _total_sample_count; uint64_t _ring_sample_count; int _unit_size; + bool _memory_failed; + bool _last_ended; }; } // namespace data diff --git a/DSView/pv/dock/protocoldock.cpp b/DSView/pv/dock/protocoldock.cpp index 087a63bb..730c6e6f 100644 --- a/DSView/pv/dock/protocoldock.cpp +++ b/DSView/pv/dock/protocoldock.cpp @@ -166,14 +166,6 @@ int ProtocolDock::decoder_name_cmp(const void *a, const void *b) ((const srd_decoder*)b)->name); } -void ProtocolDock::paintEvent(QPaintEvent *) -{ - //QStyleOption opt; - //opt.init(this); - //QPainter p(this); - //style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); -} - void ProtocolDock::add_protocol() { if (_session.get_device()->dev_inst()->mode != LOGIC) { @@ -228,7 +220,7 @@ void ProtocolDock::add_protocol() // progress connection const std::vector< boost::shared_ptr > decode_sigs( _session.get_decode_signals()); - //connect(decode_sigs.back().get(), SIGNAL(decoded_progress(int)), this, SLOT(decoded_progess(int))); + connect(decode_sigs.back().get(), SIGNAL(decoded_progress(int)), this, SLOT(decoded_progess(int))); protocol_updated(); } @@ -363,6 +355,7 @@ void ProtocolDock::decoded_progess(int progress) _progress_label_list.at(index)->setText(progress_str); index++; } + update_model(); } void ProtocolDock::set_model() diff --git a/DSView/pv/dock/protocoldock.h b/DSView/pv/dock/protocoldock.h index 8ca0d198..195f89b7 100644 --- a/DSView/pv/dock/protocoldock.h +++ b/DSView/pv/dock/protocoldock.h @@ -61,8 +61,6 @@ public: ProtocolDock(QWidget *parent, SigSession &session); ~ProtocolDock(); - void paintEvent(QPaintEvent *); - void del_all_protocol(); signals: diff --git a/DSView/pv/sigsession.cpp b/DSView/pv/sigsession.cpp index a3851d27..51141f24 100644 --- a/DSView/pv/sigsession.cpp +++ b/DSView/pv/sigsession.cpp @@ -487,6 +487,13 @@ void SigSession::start_capture(bool instant, void SigSession::stop_capture() { _instant = false; +#ifdef ENABLE_DECODE + for (vector< boost::shared_ptr >::iterator i = + _decode_traces.begin(); + i != _decode_traces.end(); + i++) + (*i)->decoder()->stop_decode(); +#endif if (get_capture_state() != Running) return; sr_session_stop(); @@ -527,7 +534,7 @@ bool SigSession::get_instant() return _instant; } -void* SigSession::get_buf(int& unit_size, uint64_t &length) +const void* SigSession::get_buf(int& unit_size, uint64_t &length) { if (_dev_inst->dev_inst()->mode == LOGIC) { const deque< boost::shared_ptr > &snapshots = @@ -597,12 +604,12 @@ void SigSession::sample_thread_proc(boost::shared_ptr dev_inst, set_capture_state(Stopped); // Confirm that SR_DF_END was received - assert(!_cur_logic_snapshot); - assert(!_cur_dso_snapshot); - assert(!_cur_analog_snapshot); + assert(_cur_logic_snapshot->last_ended()); + assert(_cur_dso_snapshot->last_ended()); + assert(_cur_analog_snapshot->last_ended()); } -void SigSession::update_data_header(const sr_dev_inst *const sdi) +void SigSession::feed_in_header(const sr_dev_inst *sdi) { GVariant *gvar; int ret; @@ -631,15 +638,12 @@ void SigSession::update_data_header(const sr_dev_inst *const sdi) // Set the sample rate of all SignalData // Logic/Analog/Dso - set< boost::shared_ptr > data_set; - BOOST_FOREACH(const boost::shared_ptr sig, _signals) { - assert(sig); - data_set.insert(sig->data()); - } - BOOST_FOREACH(boost::shared_ptr data, data_set) { - assert(data); - data->set_samplerate(_cur_samplerate); - } + if (_logic_data) + _logic_data->set_samplerate(_cur_samplerate); + if (_analog_data) + _analog_data->set_samplerate(_cur_samplerate); + if (_dso_data) + _dso_data->set_samplerate(_cur_samplerate); #ifdef ENABLE_DECODE // DecoderStack BOOST_FOREACH(const boost::shared_ptr d, _decode_traces) @@ -658,19 +662,6 @@ void SigSession::update_data_header(const sr_dev_inst *const sdi) _group_data->set_samplerate(_cur_samplerate); } -void SigSession::feed_in_header(const sr_dev_inst *sdi) -{ -#ifdef ENABLE_DECODE - for (vector< boost::shared_ptr >::iterator i = - _decode_traces.begin(); - i != _decode_traces.end(); - i++) - (*i)->decoder()->stop_decode(); -#endif - update_data_header(sdi); - //receive_data(0); -} - void SigSession::add_group() { std::list probe_index_list; @@ -786,21 +777,36 @@ void SigSession::init_signals() } } + // Create snapshots + { + _cur_logic_snapshot.reset(new data::LogicSnapshot()); + assert(_cur_logic_snapshot); + + _cur_dso_snapshot.reset(new data::DsoSnapshot()); + assert(_cur_dso_snapshot); + + _cur_analog_snapshot.reset(new data::AnalogSnapshot()); + assert(_cur_analog_snapshot); + } + // Create data containers for the coming data snapshots { if (logic_probe_count != 0) { _logic_data.reset(new data::Logic()); assert(_logic_data); + _logic_data->push_snapshot(_cur_logic_snapshot); } if (dso_probe_count != 0) { _dso_data.reset(new data::Dso()); assert(_dso_data); + _dso_data->push_snapshot(_cur_dso_snapshot); } if (analog_probe_count != 0) { _analog_data.reset(new data::Analog()); assert(_analog_data); + _analog_data->push_snapshot(_cur_analog_snapshot); } _group_data.reset(new data::Group()); @@ -914,7 +920,7 @@ void SigSession::refresh(int holdtime) { if (_logic_data) { _logic_data->clear(); - _cur_logic_snapshot.reset(); + //_cur_logic_snapshot.reset(); #ifdef ENABLE_DECODE BOOST_FOREACH(const boost::shared_ptr d, _decode_traces) { @@ -925,7 +931,8 @@ void SigSession::refresh(int holdtime) } if (_dso_data) { _dso_data->clear(); - _cur_dso_snapshot.reset(); + //_cur_dso_snapshot.reset(); + _cur_dso_snapshot->set_last_ended(true); // MathStack BOOST_FOREACH(const boost::shared_ptr m, _math_traces) { @@ -935,7 +942,7 @@ void SigSession::refresh(int holdtime) } if (_analog_data) { _analog_data->clear(); - _cur_analog_snapshot.reset(); + //_cur_analog_snapshot.reset(); } if (strncmp(_dev_inst->dev_inst()->driver->name, "virtual", 7)) { _data_lock = true; @@ -997,8 +1004,7 @@ void SigSession::feed_in_logic(const sr_datafeed_logic &logic) { boost::lock_guard lock(_data_mutex); - if (!_logic_data) - { + if (!_logic_data || !_cur_logic_snapshot) { qDebug() << "Unexpected logic packet"; return; } @@ -1007,29 +1013,20 @@ void SigSession::feed_in_logic(const sr_datafeed_logic &logic) test_data_error(); } - if (!_cur_logic_snapshot) - { - // Create a new data snapshot - _cur_logic_snapshot = boost::shared_ptr( - new data::LogicSnapshot(logic, _dev_inst->get_sample_limit(), 1)); - if (_cur_logic_snapshot->buf_null()) - { + if (_cur_logic_snapshot->last_ended()) { + _cur_logic_snapshot->first_payload(logic, _dev_inst->get_sample_limit(), 1); + if (_cur_logic_snapshot->memory_failed()) { malloc_error(); return; - } else { - _logic_data->push_snapshot(_cur_logic_snapshot); } - // @todo Putting this here means that only listeners querying // for logic will be notified. Currently the only user of // frame_began is DecoderStack, but in future we need to signal // this after both analog and logic sweeps have begun. frame_began(); - } else if(!_cur_logic_snapshot->buf_null()) { + } else { // Append to the existing data snapshot _cur_logic_snapshot->append_payload(logic); - } else { - return; } emit receive_data(logic.length/logic.unitsize); @@ -1041,13 +1038,13 @@ void SigSession::feed_in_dso(const sr_datafeed_dso &dso) { boost::lock_guard lock(_data_mutex); - if(!_dso_data) + if(!_dso_data || !_cur_dso_snapshot) { qDebug() << "Unexpected dso packet"; return; // This dso packet was not expected. } - if (!_cur_dso_snapshot) + if (_cur_dso_snapshot->last_ended()) { // reset scale of dso signal BOOST_FOREACH(const boost::shared_ptr s, _signals) @@ -1058,24 +1055,17 @@ void SigSession::feed_in_dso(const sr_datafeed_dso &dso) dsoSig->set_scale(dsoSig->get_view_rect().height() / 256.0f); } - // Create a new data snapshot - _cur_dso_snapshot = boost::shared_ptr( - new data::DsoSnapshot(dso, _dev_inst->get_sample_limit(), get_ch_num(SR_CHANNEL_DSO), _instant)); - if (_cur_dso_snapshot->buf_null()) - { + // first payload + _cur_dso_snapshot->first_payload(dso, _dev_inst->get_sample_limit(), get_ch_num(SR_CHANNEL_DSO), _instant); + if (_cur_dso_snapshot->memory_failed()) { malloc_error(); return; - } else { - _dso_data->push_snapshot(_cur_dso_snapshot); } - } else if(!_cur_dso_snapshot->buf_null()) { + } else { // Append to the existing data snapshot _cur_dso_snapshot->append_payload(dso); - } else { - return; } - - // reset scale of dso signal + // calculate related math results BOOST_FOREACH(const boost::shared_ptr m, _math_traces) { assert(m); @@ -1085,36 +1075,29 @@ void SigSession::feed_in_dso(const sr_datafeed_dso &dso) receive_data(dso.num_samples); data_updated(); - //if (!_instant) - // start_timer(ViewTime); } void SigSession::feed_in_analog(const sr_datafeed_analog &analog) { boost::lock_guard lock(_data_mutex); - if(!_analog_data) + if(!_analog_data || !_cur_analog_snapshot) { qDebug() << "Unexpected analog packet"; return; // This analog packet was not expected. } - if (!_cur_analog_snapshot) + if (_cur_analog_snapshot->last_ended()) { - // Create a new data snapshot - _cur_analog_snapshot = boost::shared_ptr( - new data::AnalogSnapshot(analog, _dev_inst->get_sample_limit(), get_ch_num(SR_CHANNEL_ANALOG))); - if (_cur_analog_snapshot->buf_null()) - { + // first payload + _cur_analog_snapshot->first_payload(analog, _dev_inst->get_sample_limit(), get_ch_num(SR_CHANNEL_ANALOG)); + if (_cur_analog_snapshot->memory_failed()) { + malloc_error(); return; - } else if(!_cur_analog_snapshot->buf_null()) { - _analog_data->push_snapshot(_cur_analog_snapshot); } - } else if(!_cur_analog_snapshot->buf_null()) { + } else { // Append to the existing data snapshot _cur_analog_snapshot->append_payload(analog); - } else { - return; } receive_data(analog.num_samples); @@ -1165,7 +1148,7 @@ void SigSession::data_feed_in(const struct sr_dev_inst *sdi, { { boost::lock_guard lock(_data_mutex); - if (_cur_logic_snapshot) { + if (!_cur_logic_snapshot->empty()) { BOOST_FOREACH(const boost::shared_ptr g, _group_traces) { assert(g); @@ -1176,9 +1159,9 @@ void SigSession::data_feed_in(const struct sr_dev_inst *sdi, _cur_group_snapshot.reset(); } } - _cur_logic_snapshot.reset(); - _cur_dso_snapshot.reset(); - _cur_analog_snapshot.reset(); + _cur_logic_snapshot->set_last_ended(true); + _cur_dso_snapshot->set_last_ended(true); + _cur_analog_snapshot->set_last_ended(true); #ifdef ENABLE_DECODE BOOST_FOREACH(const boost::shared_ptr d, _decode_traces) d->frame_ended(); diff --git a/DSView/pv/sigsession.h b/DSView/pv/sigsession.h index 55e9ec53..5092b19b 100644 --- a/DSView/pv/sigsession.h +++ b/DSView/pv/sigsession.h @@ -173,7 +173,7 @@ public: void del_group(); - void* get_buf(int& unit_size, uint64_t& length); + const void* get_buf(int& unit_size, uint64_t& length); void start_hotplug_proc(boost::function error_handler); void stop_hotplug_proc(); @@ -191,8 +191,6 @@ public: private: void set_capture_state(capture_state state); - void update_data_header(const sr_dev_inst *const sdi); - private: /** * Attempts to autodetect the format. Failing that diff --git a/DSView/pv/toolbars/filebar.cpp b/DSView/pv/toolbars/filebar.cpp index cd7e0d94..3c5efd30 100644 --- a/DSView/pv/toolbars/filebar.cpp +++ b/DSView/pv/toolbars/filebar.cpp @@ -158,7 +158,7 @@ void FileBar::show_session_error( void FileBar::on_actionExport_triggered(){ int unit_size; uint64_t length; - void* buf = _session.get_buf(unit_size, length); + const void* buf = _session.get_buf(unit_size, length); if (!buf) { QMessageBox msg(this); msg.setText(tr("Data Export")); @@ -192,7 +192,7 @@ void FileBar::on_actionSave_triggered() //save(); int unit_size; uint64_t length; - void* buf = _session.get_buf(unit_size, length); + const void* buf = _session.get_buf(unit_size, length); if (!buf) { QMessageBox msg(this); msg.setText(tr("File Save")); diff --git a/DSView/pv/view/analogsignal.cpp b/DSView/pv/view/analogsignal.cpp index de501262..d9cc46b2 100644 --- a/DSView/pv/view/analogsignal.cpp +++ b/DSView/pv/view/analogsignal.cpp @@ -94,6 +94,8 @@ void AnalogSignal::paint_mid(QPainter &p, int left, int right) _scale = _totalHeight * 1.0f / 65536; const boost::shared_ptr &snapshot = snapshots.front(); + if (snapshot->empty()) + return; if (get_index() >= (int)snapshot->get_channel_num()) return; diff --git a/DSView/pv/view/decodetrace.cpp b/DSView/pv/view/decodetrace.cpp index 8d9ea2d7..f3ba3af0 100644 --- a/DSView/pv/view/decodetrace.cpp +++ b/DSView/pv/view/decodetrace.cpp @@ -229,16 +229,10 @@ void DecodeTrace::paint_mid(QPainter &p, int left, int right) const QString err = _decoder_stack->error_message(); if (!err.isEmpty()) { - //draw_unresolved_period(p, annotation_height, left, right, - // samples_per_pixel, pixels_offset); draw_error(p, err, left, right); return; } - // Draw the hatching - if (draw_unresolved_period(p, annotation_height, left, right)) - return; - // Iterate through the rows assert(_view); int y = get_y() - (_totalHeight - annotation_height)*0.5; @@ -637,40 +631,6 @@ void DecodeTrace::draw_error(QPainter &p, const QString &message, p.drawText(text_rect, message); } -bool DecodeTrace::draw_unresolved_period(QPainter &p, int h, int left, - int right) -{ - using namespace pv::data; - using pv::data::decode::Decoder; - - assert(_decoder_stack); - - boost::shared_ptr data; - boost::shared_ptr logic_signal; - - //const int64_t need_sample_count = _decoder_stack->sample_count(); - const uint64_t need_sample_count = _decode_end - _decode_start + 1; - if (need_sample_count == 0) - return true; - - const uint64_t samples_decoded = _decoder_stack->samples_decoded(); - if (need_sample_count == samples_decoded) - return false; - - const int y = get_y(); - const QRectF no_decode_rect(left, y - h/2 + 0.5, right - left, h); - - const int progress100 = ceil(samples_decoded * 100.0 / need_sample_count); - p.setPen(dsLightBlue); - QFont font=p.font(); - font.setPointSize(_view->get_signalHeight()*2/3); - font.setBold(true); - p.setFont(font); - p.drawText(no_decode_rect, Qt::AlignCenter | Qt::AlignVCenter, QString::number(_progress)+"%"); - - return true; -} - void DecodeTrace::draw_unshown_row(QPainter &p, int y, int h, int left, int right, QString info) { @@ -830,12 +790,13 @@ void DecodeTrace::commit_probes() void DecodeTrace::on_new_decode_data() { - const uint64_t need_sample_count = _decode_end - _decode_start + 1; - if (need_sample_count == 0) { + uint64_t real_end = min(_decoder_stack->sample_count(), _decode_end+1); + const int64_t need_sample_count = real_end - _decode_start; + if (need_sample_count <= 0) { _progress = 100; } else { const uint64_t samples_decoded = _decoder_stack->samples_decoded(); - _progress = ceil(samples_decoded * 100.0 / need_sample_count); + _progress = floor(samples_decoded * 100.0 / need_sample_count); } decoded_progress(_progress); @@ -850,10 +811,11 @@ int DecodeTrace::get_progress() const void DecodeTrace::on_decode_done() { - if (_view) { - _view->set_update(_viewport, true); - _view->signals_changed(); - } +// if (_view) { +// _view->set_update(_viewport, true); +// _view->signals_changed(); +// } + on_new_decode_data(); _session.decode_done(); } diff --git a/DSView/pv/view/decodetrace.h b/DSView/pv/view/decodetrace.h index 41cc94fe..39db68e6 100644 --- a/DSView/pv/view/decodetrace.h +++ b/DSView/pv/view/decodetrace.h @@ -170,9 +170,6 @@ private: void draw_error(QPainter &p, const QString &message, int left, int right); - bool draw_unresolved_period(QPainter &p, int h, int left, - int right); - void draw_unshown_row(QPainter &p, int y, int h, int left, int right, QString info); diff --git a/DSView/pv/view/dsosignal.cpp b/DSView/pv/view/dsosignal.cpp index f2a4b66b..4113c65f 100644 --- a/DSView/pv/view/dsosignal.cpp +++ b/DSView/pv/view/dsosignal.cpp @@ -305,7 +305,7 @@ bool DsoSignal::go_hDialPre(bool setted) { int ch_num = _view->session().get_ch_num(SR_CHANNEL_DSO); if (ch_num != 0 && !_hDial->isMin()) { - uint64_t sample_rate = _view->session().cur_samplerate(); + uint64_t sample_rate = _view->session().get_device()->get_sample_rate(); const uint64_t min_div = std::pow(10.0, 9.0) / sample_rate; if (_view->session().get_capture_state() != SigSession::Running && !_data->get_snapshots().empty()) { @@ -764,8 +764,8 @@ void DsoSignal::paint_back(QPainter &p, int left, int right) p.setPen(Trace::dsLightBlue); p.drawLine(left, UpMargin/2, left + width, UpMargin/2); - const uint64_t sample_len = _view->session().cur_samplelimits(); - const double samplerate = _view->session().cur_samplerate(); + const uint64_t sample_len = _view->session().get_device()->get_sample_limit(); + const double samplerate = _view->session().get_device()->get_sample_rate(); const double samples_per_pixel = samplerate * _view->scale(); const double shown_rate = min(samples_per_pixel * width * 1.0 / sample_len, 1.0); const double start_time = _data->get_start_time(); @@ -835,6 +835,8 @@ void DsoSignal::paint_mid(QPainter &p, int left, int right) return; const boost::shared_ptr &snapshot = snapshots.front(); + if (snapshot->empty()) + return; const uint16_t number_channels = snapshot->get_channel_num(); if ((strcmp(_dev_inst->dev_inst()->driver->name, "DSLogic") == 0) && @@ -843,7 +845,7 @@ void DsoSignal::paint_mid(QPainter &p, int left, int right) const double pixels_offset = offset / scale; //const double samplerate = _data->samplerate(); - const double samplerate = _view->session().cur_samplerate(); + const double samplerate = _view->session().get_device()->get_sample_rate(); const double start_time = _data->get_start_time(); const int64_t last_sample = max((int64_t)(snapshot->get_sample_count() - 1), (int64_t)0); const double samples_per_pixel = samplerate * scale; @@ -1280,7 +1282,7 @@ void DsoSignal::paint_measure(QPainter &p) double value_p2p = value_max - value_min; _period = (count == 0) ? period * 10.0 : period * 10.0 / count; const int channel_count = _view->session().get_ch_num(SR_CHANNEL_DSO); - uint64_t sample_rate = _view->session().cur_samplerate(); + uint64_t sample_rate = _view->session().get_device()->get_sample_rate(); _period = _period * 200.0 / (channel_count * sample_rate * 1.0 / SR_MHZ(1)); _ms_string[DSO_MS_VMAX] = "Vmax: " + (abs(value_max) > 1000 ? QString::number(value_max/1000.0, 'f', 2) + "V" : QString::number(value_max, 'f', 2) + "mV"); _ms_string[DSO_MS_VMIN] = "Vmin: " + (abs(value_min) > 1000 ? QString::number(value_min/1000.0, 'f', 2) + "V" : QString::number(value_min, 'f', 2) + "mV"); @@ -1448,14 +1450,14 @@ bool DsoSignal::measure(const QPointF &p) const boost::shared_ptr &snapshot = snapshots.front(); - if (snapshot->buf_null()) + if (snapshot->empty()) return false; const double scale = _view->scale(); assert(scale > 0); const double offset = _view->offset(); const double pixels_offset = offset / scale; - const double samplerate = _view->session().cur_samplerate(); + const double samplerate = _view->session().get_device()->get_sample_rate(); const double samples_per_pixel = samplerate * scale; _hover_index = floor((p.x() + pixels_offset) * samples_per_pixel+0.5); diff --git a/DSView/pv/view/logicsignal.cpp b/DSView/pv/view/logicsignal.cpp index 94e96d7b..fb911fd7 100644 --- a/DSView/pv/view/logicsignal.cpp +++ b/DSView/pv/view/logicsignal.cpp @@ -142,23 +142,18 @@ void LogicSignal::paint_mid(QPainter &p, int left, int right) const deque< boost::shared_ptr > &snapshots = _data->get_snapshots(); - if (snapshots.empty()) + double samplerate = _data->samplerate(); + if (snapshots.empty() || samplerate == 0) return; const boost::shared_ptr &snapshot = snapshots.front(); - if (snapshot->buf_null()) + if (snapshot->empty()) return; - double samplerate = _data->samplerate(); - - // Show sample rate as 1Hz when it is unknown - if (samplerate == 0.0) - samplerate = 1.0; - const double pixels_offset = offset / scale; const double start_time = _data->get_start_time(); - const int64_t last_sample = snapshot->get_sample_count() - 1; + const int64_t last_sample = snapshot->get_sample_count() - 1; const double samples_per_pixel = samplerate * scale; const double start = samplerate * (offset - start_time); const double end = start + samples_per_pixel * (right - left); @@ -308,7 +303,7 @@ bool LogicSignal::measure(const QPointF &p, uint64_t &index0, uint64_t &index1, const boost::shared_ptr &snapshot = snapshots.front(); - if (snapshot->buf_null()) + if (snapshot->empty()) return false; uint64_t index = _data->samplerate() * (_view->offset() - _data->get_start_time() + p.x() * _view->scale()); @@ -353,7 +348,7 @@ bool LogicSignal::edges(const QPointF &p, uint64_t start, uint64_t &rising, uint const boost::shared_ptr &snapshot = snapshots.front(); - if (snapshot->buf_null()) + if (snapshot->empty()) return false; end = _data->samplerate() * (_view->offset() - _data->get_start_time() + p.x() * _view->scale()); From 093824e610532e6b78ae42ead5dd834ea07454f8 Mon Sep 17 00:00:00 2001 From: DreamSourceLab Date: Tue, 17 May 2016 17:53:01 +0800 Subject: [PATCH 17/32] Add search function for protocol list viewer @ LA mode --- DSView/darkstyle/style.qss | 2 +- DSView/icons/next.png | Bin 699 -> 465 bytes DSView/icons/pre.png | Bin 681 -> 475 bytes DSView/pv/data/decoderstack.cpp | 5 + DSView/pv/data/decoderstack.h | 2 + DSView/pv/dock/protocoldock.cpp | 163 ++++++++++++++++++++++++++++++-- DSView/pv/dock/protocoldock.h | 13 ++- DSView/pv/dock/searchdock.cpp | 6 +- DSView/pv/view/decodetrace.cpp | 4 +- DSView/pv/view/header.cpp | 2 + DSView/pv/view/ruler.cpp | 1 + DSView/pv/view/viewport.cpp | 1 + 12 files changed, 184 insertions(+), 15 deletions(-) mode change 100644 => 100755 DSView/icons/next.png mode change 100644 => 100755 DSView/icons/pre.png diff --git a/DSView/darkstyle/style.qss b/DSView/darkstyle/style.qss index cacf5e26..fdb48dec 100755 --- a/DSView/darkstyle/style.qss +++ b/DSView/darkstyle/style.qss @@ -611,7 +611,7 @@ background-color: QLinearGradient( x1: 0, y1: 0, x2: 0, y2: 1, QComboBox:hover,QAbstractSpinBox:hover,QLineEdit:hover,QTextEdit:hover,QPlainTextEdit:hover,QAbstractView:hover,QTreeView:hover { - border: 1px solid #78879b; + border: 1px solid #606060; color: silver; } diff --git a/DSView/icons/next.png b/DSView/icons/next.png old mode 100644 new mode 100755 index cc35544b3e7214988506aca02f398494c7b80ab6..d3efe6f2e90787d139ef2069634b1c6d9b7a6c86 GIT binary patch literal 465 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE0wix1Z>k4UEa{HEjtmSN`?>!lvVtU&J%W50 z7^>757#dm_7=8hT8eT9klo~KFyh>nTu$sZZAYL$MSD+10f+@+{-GzZ+Rj;xUkjGiz z5m^kh={g8AI%&+V01C2~c>21sKVTK)R^r>%)s+Vnn(yi27~*k!>(t%64F)_e$}Y^> zhJwrn?6S8H@PzanVEN+i=vTRMnMKcL`+PY&^kwro-lThsM)%rzzPv4jHv!g1^>iava{kb>sp2kq+6f zwqdN-{`oiRDBW}`ns6deZF*-+`lkgG)O&4KeTh_;Kh3_rw`QIGz2BVsTqnBSnpMRK zbgF8JYeY#(Vo9o1a#1RfVlXl=G}ARO)ipE^F*LL?HnB1^(Kax$GBDs%TnP*{Bn`Rw zDVb@NxHZ(*2z~@=kObKfoS#-wo>-L1P+nfHmzkGcoSayYs+V7sKKq@G6i^X^r>mdK II;Vst0ODSc-T(jq delta 686 zcmV;f0#W_Z1G@!~8Gi-<0047(dh`GQ03c&XQcVB=dL;k=fP(-4`Tqa_faw4Lbua(` z>RI+y?e7jKeZ#YO-C00v@9M??Vs0RI60puMM)00009a7bBm000XU000XU0RWnu z7ytkQFG)l}R9M5smtU&dKorKmQwoBjv5KfD(g)wYOUMqqJAW`sa0%T(?+&irLA!#w z1p6)+#XqPh6E#6m^Yujs`_MLt{|1I6182V9oSB?CM@ZtC?E?Ua2t))V>m+-;f9sK~ ziwJ}yLl55VUK1K2_m`~BVkpdKs^;B*E6fMn3#0q{z)6T;@N zU2nm5yAAR?bbmy|jKyNkZnrZ?wgJ36(gW>SEam{f>1mNvH}Qbyc|ik$6_3Zioj*L! zs{v>ONJsI225?23W7xVMC&oOeQtNX0vIKY()lGtyWQDvXx4u zG{kDPY5)iYp#dDni2|Sin1*5eIuAIG6AD5DD5av_N`DG~nNFuQgi`7qfE5a0xm;dz zEh+$JCX>++%jGfq18pCStt~= z_+&C^_cH>3_brCu|XER;$$?^6c{bH-Tb= UYB>QoUH||907*qoM6N<$g2Eps)Bpeg diff --git a/DSView/icons/pre.png b/DSView/icons/pre.png old mode 100644 new mode 100755 index f7ccd2ebfe11c3dfb2733134678f602571a97923..ee773259723afbb3bf40760dc295c17407e988a9 GIT binary patch literal 475 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE0wix1Z>k4UEa{HEjtmSN`?>!lvVtU&J%W50 z7^>757#dm_7=8hT8eT9klo~KFyh>nTu$sZZAYL$MSD+10f+@+{-GzZ+Rj;xUkjGiz z5m^kh={g8AI%&+V01C2~c>21sKVTK)R)`KIByi;Qf z9v(>Of6&*zVr`SgI&P5|;T07z4i5|&TiaPx#$<$ z6TSP|7B-8VrDVBIE}X*Kb>pwme&dxFKFzs#iGwxp?ati4Yds`%{9P|)?ACs^g@dK> z<38pYl@r~*NACOtbhK)TYeY#(Vo9o1a#1RfVlXl=G}ARO)ipE^F*LL?HnuXg&^9o# zGB8*#er*?uhTQy=%(P0}8t!@Ju>mzmf@}!RPb(=;EJ|f4FE7{2%*!rLPAo{(%P&fw T{mw=TsEEPS)z4*}Q$iB}Q{kJ* delta 668 zcmV;N0%QH#1E~d&8Gi-<0047(dh`GQ03c&XQcVB=dL;k=fP(-4`Tqa_faw4Lbua(` z>RI+y?e7jKeZ#YO-C00v@9M??Vs0RI60puMM)00009a7bBm000XU000XU0RWnu z7ytkQ9Z5t%R9M5sS6hnWKomW97zm;gQ9&gj^Wnd}gzUiHfq&^8xP;k3_YTJ1!ORX^ zg8eTf5noY|L{StqXMVJhnV&p-T&RyKxOGpR`>HD>@l5#!03rer0m&9goA(!+WJ^RK zBq1U=olY$PzaLD%s{(L59&G?W9t^+{k~ka=IspH{#sQpT002ndV)X&MlI%y-Q7+i+ zc4^b0FCwO6j0K7bk0Uv;+X<7gPnx+LLdr@@1Ujs-eJ^(Y5$$VW7w%cu+ zWG9MpS^z!(Gn>sSkSbV!|mdj-`48z36 z<1)}Il}c~d2WeiO1i*iCi@!viN@y+?i(mifn>7 z$Kxa+?SBI>4Z~3VU_2hT0YqjZ?SG&^vNsxynwNVF!-&O%VqiENrb%iafLX8C6%)hZ z@Et(s-V9tb(RE!{2LRV~^>cbgXTWisTZ~A306)*`C5nOqVB7XnX0+>G;NS^^$u)ou z9zU9OlnVf0Fc`GXKbw0q;fP45(P;dUTeQ?P@)Z}zEt!?dFvaiy0000 wait_for_data() const; diff --git a/DSView/pv/dock/protocoldock.cpp b/DSView/pv/dock/protocoldock.cpp index 730c6e6f..e57ea47a 100644 --- a/DSView/pv/dock/protocoldock.cpp +++ b/DSView/pv/dock/protocoldock.cpp @@ -38,6 +38,8 @@ #include #include #include +#include +#include #include #include @@ -47,7 +49,8 @@ namespace dock { ProtocolDock::ProtocolDock(QWidget *parent, SigSession &session) : QScrollArea(parent), - _session(session) + _session(session), + _cur_search_index(-1) { _up_widget = new QWidget(this); @@ -93,7 +96,7 @@ ProtocolDock::ProtocolDock(QWidget *parent, SigSession &session) : _up_layout->addStretch(1); _up_widget->setLayout(_up_layout); - _up_widget->setMinimumHeight(120); + _up_widget->setMinimumHeight(150); // this->setWidget(_widget); // _widget->setGeometry(0, 0, sizeHint().width(), 500); @@ -129,12 +132,53 @@ ProtocolDock::ProtocolDock(QWidget *parent, SigSession &session) : _table_view->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel); _table_view->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); + _pre_button = new QPushButton(_dn_widget); + _nxt_button = new QPushButton(_dn_widget); + _pre_button->setIcon(QIcon::fromTheme("protocol", + QIcon(":/icons/pre.png"))); + _nxt_button->setIcon(QIcon::fromTheme("protocol", + QIcon(":/icons/next.png"))); + connect(_pre_button, SIGNAL(clicked()), + this, SLOT(search_pre())); + connect(_nxt_button, SIGNAL(clicked()), + this, SLOT(search_nxt())); + + QPushButton *search_button = new QPushButton(this); + search_button->setIcon(QIcon::fromTheme("protocol", + QIcon(":/icons/search.png"))); + search_button->setFixedWidth(search_button->height()); + search_button->setDisabled(true); + _search_edit = new QLineEdit(_dn_widget); + _search_edit->setPlaceholderText(tr("search")); + QHBoxLayout *search_layout = new QHBoxLayout(); + search_layout->addWidget(search_button); + search_layout->addStretch(); + search_layout->setContentsMargins(0, 0, 0, 0); + _search_edit->setLayout(search_layout); + _search_edit->setTextMargins(search_button->width(), 0, 0, 0); + QSizePolicy sp = _search_edit->sizePolicy(); + sp.setHorizontalStretch(1); + _search_edit->setSizePolicy(sp); + + QHBoxLayout *dn_search_layout = new QHBoxLayout(); + dn_search_layout->addWidget(_pre_button, 0, Qt::AlignLeft); + dn_search_layout->addWidget(_search_edit, 0, Qt::AlignLeft); + dn_search_layout->addWidget(_nxt_button, 0, Qt::AlignRight); + + _matchs_label = new QLabel(_dn_widget); + QHBoxLayout *dn_match_layout = new QHBoxLayout(); + dn_match_layout->addWidget(new QLabel(tr("Matching Items:")), 0, Qt::AlignLeft); + dn_match_layout->addWidget(_matchs_label, 0, Qt::AlignLeft); + dn_match_layout->addStretch(1); + QVBoxLayout *dn_layout = new QVBoxLayout(); dn_layout->addLayout(dn_title_layout); + dn_layout->addLayout(dn_search_layout); + dn_layout->addLayout(dn_match_layout); dn_layout->addWidget(_table_view); _dn_widget->setLayout(dn_layout); - _dn_widget->setMinimumHeight(400); + _dn_widget->setMinimumHeight(350); _split_widget = new QSplitter(this); _split_widget->insertWidget(0, _up_widget); @@ -154,12 +198,22 @@ ProtocolDock::ProtocolDock(QWidget *parent, SigSession &session) : connect(this, SIGNAL(protocol_updated()), this, SLOT(update_model())); connect(_table_view, SIGNAL(clicked(QModelIndex)), this, SLOT(item_clicked(QModelIndex))); connect(_table_view->horizontalHeader(), SIGNAL(sectionResized(int,int,int)), this, SLOT(column_resize(int, int, int))); + //connect(_table_view->verticalScrollBar(), SIGNAL(sliderMoved()), this, SLOT(sliderMoved())); + connect(_search_edit, SIGNAL(editingFinished()), this, SLOT(search_done())); } ProtocolDock::~ProtocolDock() { } +void ProtocolDock::paintEvent(QPaintEvent *) +{ +// QStyleOption opt; +// opt.init(this); +// QPainter p(this); +// style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); +} + int ProtocolDock::decoder_name_cmp(const void *a, const void *b) { return strcmp(((const srd_decoder*)a)->name, @@ -220,7 +274,7 @@ void ProtocolDock::add_protocol() // progress connection const std::vector< boost::shared_ptr > decode_sigs( _session.get_decode_signals()); - connect(decode_sigs.back().get(), SIGNAL(decoded_progress(int)), this, SLOT(decoded_progess(int))); + connect(decode_sigs.back().get(), SIGNAL(decoded_progress(int)), this, SLOT(decoded_progress(int))); protocol_updated(); } @@ -339,23 +393,29 @@ void ProtocolDock::del_all_protocol() } } -void ProtocolDock::decoded_progess(int progress) +void ProtocolDock::decoded_progress(int progress) { (void) progress; + int pg; + QString err=""; const std::vector< boost::shared_ptr > decode_sigs( _session.get_decode_signals()); int index = 0; BOOST_FOREACH(boost::shared_ptr d, decode_sigs) { - QString progress_str = QString::number(d->get_progress()) + "%"; - if (d->get_progress() == 100) + pg = d->get_progress(); + if (d->decoder()->out_of_memory()) + err = tr("(Out of Memory)"); + QString progress_str = QString::number(pg) + "%" + err; + if (pg == 100) _progress_label_list.at(index)->setStyleSheet("color:green;"); else _progress_label_list.at(index)->setStyleSheet("color:red;"); _progress_label_list.at(index)->setText(progress_str); index++; } - update_model(); + if (pg == 0 || pg % 10 == 1) + update_model(); } void ProtocolDock::set_model() @@ -363,6 +423,8 @@ void ProtocolDock::set_model() pv::dialogs::ProtocolList *protocollist_dlg = new pv::dialogs::ProtocolList(this, _session); protocollist_dlg->exec(); resize_table_view(_session.get_decoder_model()); + _model_proxy.setSourceModel(_session.get_decoder_model()); + search_done(); } void ProtocolDock::update_model() @@ -386,7 +448,8 @@ void ProtocolDock::update_model() if (index >= decode_sigs.size()) decoder_model->setDecoderStack(decode_sigs.at(0)->decoder()); } - + _model_proxy.setSourceModel(decoder_model); + search_done(); resize_table_view(decoder_model); } @@ -417,10 +480,50 @@ void ProtocolDock::item_clicked(const QModelIndex &index) _session.show_region(ann.start_sample(), ann.end_sample()); } } + _table_view->resizeRowToContents(index.row()); + if (index.column() != _model_proxy.filterKeyColumn()) { + _model_proxy.setFilterKeyColumn(index.column()); + _model_proxy.setSourceModel(decoder_model); + search_done(); + } + QModelIndex filterIndex = _model_proxy.mapFromSource(index); + if (filterIndex.isValid()) { + _cur_search_index = filterIndex.row(); + } else { + if (_model_proxy.rowCount() == 0) { + _cur_search_index = -1; + } else { + uint64_t up = 0; + uint64_t dn = _model_proxy.rowCount() - 1; + do { + uint64_t md = (up + dn)/2; + QModelIndex curIndex = _model_proxy.mapToSource(_model_proxy.index(md,_model_proxy.filterKeyColumn())); + if (index.row() == curIndex.row()) { + _cur_search_index = md; + break; + } else if (md == up) { + if (curIndex.row() < index.row() && up < dn) { + QModelIndex nxtIndex = _model_proxy.mapToSource(_model_proxy.index(md+1,_model_proxy.filterKeyColumn())); + if (nxtIndex.row() < index.row()) + md++; + } + _cur_search_index = md + ((curIndex.row() < index.row()) ? 0.5 : -0.5); + break; + } else if (curIndex.row() < index.row()) { + up = md; + } else if (curIndex.row() > index.row()) { + dn = md; + } + }while(1); + } + } } void ProtocolDock::column_resize(int index, int old_size, int new_size) { + (void)index; + (void)old_size; + (void)new_size; pv::data::DecoderModel *decoder_model = _session.get_decoder_model(); if (decoder_model->getDecoderStack()) { int top_row = _table_view->rowAt(0); @@ -438,5 +541,47 @@ void ProtocolDock::export_table_view() protocolexp_dlg->exec(); } +void ProtocolDock::search_pre() +{ + // now the proxy only contains rows that match the name + // let's take the pre one and map it to the original model + if (_model_proxy.rowCount() == 0) + return; + _cur_search_index -= 1; + if (_cur_search_index <= -1 || _cur_search_index >= _model_proxy.rowCount()) + _cur_search_index = _model_proxy.rowCount() - 1; + QModelIndex matchingIndex = _model_proxy.mapToSource(_model_proxy.index(ceil(_cur_search_index),_model_proxy.filterKeyColumn())); + if(matchingIndex.isValid()){ + _table_view->scrollTo(matchingIndex); + _table_view->setCurrentIndex(matchingIndex); + _table_view->clicked(matchingIndex); + } +} + +void ProtocolDock::search_nxt() +{ + // now the proxy only contains rows that match the name + // let's take the pre one and map it to the original model + if (_model_proxy.rowCount() == 0) + return; + _cur_search_index += 1; + if (_cur_search_index < 0 || _cur_search_index >= _model_proxy.rowCount()) + _cur_search_index = 0; + QModelIndex matchingIndex = _model_proxy.mapToSource(_model_proxy.index(floor(_cur_search_index),_model_proxy.filterKeyColumn())); + if(matchingIndex.isValid()){ + _table_view->scrollTo(matchingIndex); + _table_view->setCurrentIndex(matchingIndex); + _table_view->clicked(matchingIndex); + } +} + +void ProtocolDock::search_done() +{ + QString str = _search_edit->text().trimmed(); + _model_proxy.setFilterFixedString(str); + _matchs_label->setText(QString::number(_model_proxy.rowCount())); +} + + } // namespace dock } // namespace pv diff --git a/DSView/pv/dock/protocoldock.h b/DSView/pv/dock/protocoldock.h index 195f89b7..dcd1dfd8 100644 --- a/DSView/pv/dock/protocoldock.h +++ b/DSView/pv/dock/protocoldock.h @@ -36,6 +36,7 @@ #include #include #include +#include #include @@ -60,6 +61,7 @@ class ProtocolDock : public QScrollArea public: ProtocolDock(QWidget *parent, SigSession &session); ~ProtocolDock(); + void paintEvent(QPaintEvent *); void del_all_protocol(); @@ -70,12 +72,15 @@ private slots: void add_protocol(); void rst_protocol(); void del_protocol(); - void decoded_progess(int progress); + void decoded_progress(int progress); void set_model(); void update_model(); void export_table_view(); void item_clicked(const QModelIndex &index); void column_resize(int index, int old_size, int new_size); + void search_pre(); + void search_nxt(); + void search_done(); private: static int decoder_name_cmp(const void *a, const void *b); @@ -83,11 +88,17 @@ private: private: SigSession &_session; + QSortFilterProxyModel _model_proxy; + double _cur_search_index; QSplitter *_split_widget; QWidget *_up_widget; QWidget *_dn_widget; QTableView *_table_view; + QPushButton *_pre_button; + QPushButton *_nxt_button; + QLineEdit *_search_edit; + QLabel *_matchs_label; QPushButton *_add_button; QPushButton *_del_all_button; diff --git a/DSView/pv/dock/searchdock.cpp b/DSView/pv/dock/searchdock.cpp index 3233b825..17fe110c 100644 --- a/DSView/pv/dock/searchdock.cpp +++ b/DSView/pv/dock/searchdock.cpp @@ -60,13 +60,13 @@ SearchDock::SearchDock(QWidget *parent, View &view, SigSession &session) : connect(&_nxt_button, SIGNAL(clicked()), this, SLOT(on_next())); - _pre_button.setIcon(QIcon::fromTheme("search", + _pre_button.setIcon(QIcon::fromTheme("searchDock", QIcon(":/icons/pre.png"))); - _nxt_button.setIcon(QIcon::fromTheme("search", + _nxt_button.setIcon(QIcon::fromTheme("searchDock", QIcon(":/icons/next.png"))); QPushButton *_search_button = new QPushButton(this); - _search_button->setIcon(QIcon::fromTheme("search", + _search_button->setIcon(QIcon::fromTheme("searchDock", QIcon(":/icons/search.png"))); _search_button->setFixedWidth(_search_button->height()); _search_button->setDisabled(true); diff --git a/DSView/pv/view/decodetrace.cpp b/DSView/pv/view/decodetrace.cpp index f3ba3af0..b47de76c 100644 --- a/DSView/pv/view/decodetrace.cpp +++ b/DSView/pv/view/decodetrace.cpp @@ -792,7 +792,9 @@ void DecodeTrace::on_new_decode_data() { uint64_t real_end = min(_decoder_stack->sample_count(), _decode_end+1); const int64_t need_sample_count = real_end - _decode_start; - if (need_sample_count <= 0) { + if (real_end == 0) { + _progress = 0; + } else if (need_sample_count <= 0) { _progress = 100; } else { const uint64_t samples_decoded = _decoder_stack->samples_decoded(); diff --git a/DSView/pv/view/header.cpp b/DSView/pv/view/header.cpp index 7ccb9828..a490805b 100644 --- a/DSView/pv/view/header.cpp +++ b/DSView/pv/view/header.cpp @@ -119,6 +119,8 @@ void Header::paintEvent(QPaintEvent*) //painter.setRenderHint(QPainter::Antialiasing); style()->drawPrimitive(QStyle::PE_Widget, &o, &painter, this); + painter.begin(this); + const int w = width(); const vector< boost::shared_ptr > traces( _view.get_traces(ALL_VIEW)); diff --git a/DSView/pv/view/ruler.cpp b/DSView/pv/view/ruler.cpp index 61963fed..171d99de 100644 --- a/DSView/pv/view/ruler.cpp +++ b/DSView/pv/view/ruler.cpp @@ -182,6 +182,7 @@ void Ruler::paintEvent(QPaintEvent*) QPainter p(this); style()->drawPrimitive(QStyle::PE_Widget, &o, &p, this); + p.begin(this); //QPainter p(this); //p.setRenderHint(QPainter::Antialiasing); diff --git a/DSView/pv/view/viewport.cpp b/DSView/pv/view/viewport.cpp index 99263a85..7d5c87f7 100644 --- a/DSView/pv/view/viewport.cpp +++ b/DSView/pv/view/viewport.cpp @@ -128,6 +128,7 @@ void Viewport::paintEvent(QPaintEvent *event) QPainter p(this); style()->drawPrimitive(QStyle::PE_Widget, &o, &p, this); + p.begin(this); const vector< boost::shared_ptr > traces(_view.get_traces(_type)); BOOST_FOREACH(const boost::shared_ptr t, traces) { From 9bc3bf7e6e8384d5cdd323d396155bf092028569 Mon Sep 17 00:00:00 2001 From: DreamSourceLab Date: Tue, 24 May 2016 10:03:28 +0800 Subject: [PATCH 18/32] Improve performance of protocol decoder --- DSView/dsapplication.cpp | 45 ++++++++++++++++++++++++++++++++++++++++ DSView/dsapplication.h | 37 +++++++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+) create mode 100644 DSView/dsapplication.cpp create mode 100644 DSView/dsapplication.h diff --git a/DSView/dsapplication.cpp b/DSView/dsapplication.cpp new file mode 100644 index 00000000..1a8df330 --- /dev/null +++ b/DSView/dsapplication.cpp @@ -0,0 +1,45 @@ +/* + * This file is part of the DSView project. + * DSView is based on PulseView. + * + * Copyright (C) 2016 DreamSourceLab + * + * 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 "dsapplication.h" + +#include + +DSApplication::DSApplication(int &argc, char **argv): + QApplication(argc, argv) +{ +} + +bool DSApplication::notify(QObject *receiver_, QEvent *event_) +{ + try { + return QApplication::notify(receiver_, event_); + } catch ( std::exception& e ) { + QMessageBox msg(NULL); + msg.setText("Application Error"); + msg.setInformativeText(e.what()); + msg.setStandardButtons(QMessageBox::Ok); + msg.setIcon(QMessageBox::Warning); + msg.exec(); + //QMessageBox::warning(NULL, "Application Error", e.what()) + return false; + } +} diff --git a/DSView/dsapplication.h b/DSView/dsapplication.h new file mode 100644 index 00000000..7e0b3922 --- /dev/null +++ b/DSView/dsapplication.h @@ -0,0 +1,37 @@ +/* + * This file is part of the DSView project. + * DSView is based on PulseView. + * + * Copyright (C) 2016 DreamSourceLab + * + * 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 + */ + +#ifndef DSVIEW_DSAPPLICATION_H +#define DSVIEW_DSAPPLICATION_H + +#include +#include + +class DSApplication : public QApplication +{ +public: + DSApplication(int &argc, char ** argv); + // ~MyApplication(); +private: + bool notify(QObject *receiver_, QEvent *event_); +}; + +#endif // DSVIEW_DSAPPLICATION_H From 8a261384431c4523cb1e49d88b232cfeca858ecf Mon Sep 17 00:00:00 2001 From: DreamSourceLab Date: Tue, 24 May 2016 10:04:07 +0800 Subject: [PATCH 19/32] Improve performance of protocol decoder --- DSView/main.cpp | 3 +- DSView/pv/data/decode/row.cpp | 4 +- DSView/pv/data/decoderstack.cpp | 63 +++++++++++++++--- DSView/pv/data/decoderstack.h | 14 ++-- DSView/pv/data/logicsnapshot.cpp | 20 +++--- DSView/pv/data/snapshot.cpp | 2 + DSView/pv/dock/dsotriggerdock.cpp | 8 +-- DSView/pv/dock/measuredock.cpp | 8 +-- DSView/pv/dock/searchdock.cpp | 8 +-- DSView/pv/dock/triggerdock.cpp | 8 +-- DSView/pv/mainwindow.cpp | 2 +- DSView/pv/view/decodetrace.cpp | 103 ++++++++++++++++++++---------- DSView/pv/view/header.cpp | 2 +- DSView/pv/view/logicsignal.cpp | 2 + DSView/pv/view/ruler.cpp | 2 +- DSView/pv/view/viewport.cpp | 2 +- 16 files changed, 166 insertions(+), 85 deletions(-) diff --git a/DSView/main.cpp b/DSView/main.cpp index 60876c69..07bf54a7 100644 --- a/DSView/main.cpp +++ b/DSView/main.cpp @@ -35,6 +35,7 @@ #include #include +#include "dsapplication.h" #include "pv/devicemanager.h" #include "pv/mainwindow.h" @@ -59,7 +60,7 @@ int main(int argc, char *argv[]) struct sr_context *sr_ctx = NULL; const char *open_file = NULL; - QApplication a(argc, argv); + DSApplication a(argc, argv); // Language #ifdef LANGUAGE_ZH_CN diff --git a/DSView/pv/data/decode/row.cpp b/DSView/pv/data/decode/row.cpp index 06ae2987..46fb337f 100644 --- a/DSView/pv/data/decode/row.cpp +++ b/DSView/pv/data/decode/row.cpp @@ -68,8 +68,8 @@ const QString Row::title() const bool Row::operator<(const Row &other) const { - return (_decoder->name < other._decoder->name) || - (_decoder->name == other._decoder->name && _row->desc < other._row->desc); + return (_decoder < other._decoder) || + (_decoder == other._decoder && _row < other._row); } } // decode diff --git a/DSView/pv/data/decoderstack.cpp b/DSView/pv/data/decoderstack.cpp index beaf54a5..7615e0e2 100644 --- a/DSView/pv/data/decoderstack.cpp +++ b/DSView/pv/data/decoderstack.cpp @@ -17,12 +17,11 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include - #include #include #include +#include #include @@ -57,6 +56,7 @@ namespace data { const double DecoderStack::DecodeMargin = 1.0; const double DecoderStack::DecodeThreshold = 0.2; const int64_t DecoderStack::DecodeChunkLength = 4 * 1024; +//const int64_t DecoderStack::DecodeChunkLength = 1024 * 1024; const unsigned int DecoderStack::DecodeNotifyPeriod = 1024; mutex DecoderStack::_global_decode_mutex; @@ -443,11 +443,22 @@ void DecoderStack::decode_data( uint64_t notify_cnt = (decode_end - decode_start + 1)/100; const uint64_t chunk_sample_count = DecodeChunkLength / _snapshot->unit_size(); + srd_decoder_inst *logic_di = NULL; + // find the first level decoder instant + for (GSList *d = session->di_list; d; d = d->next) { + srd_decoder_inst *di = (srd_decoder_inst *)d->data; + srd_decoder *decoder = di->decoder; + const bool have_probes = (decoder->channels || decoder->opt_channels) != 0; + if (have_probes) { + logic_di = di; + break; + } + } - for (uint64_t i = decode_start; - !boost::this_thread::interruption_requested() && - i < decode_end && !_no_memory; - i += chunk_sample_count) + uint8_t chunk_type = 0; + uint64_t i = decode_start; + while(!boost::this_thread::interruption_requested() && + i < decode_end && !_no_memory) { //lock_guard decode_lock(_global_decode_mutex); @@ -455,22 +466,56 @@ void DecoderStack::decode_data( i + chunk_sample_count, decode_end); chunk = _snapshot->get_samples(i, chunk_end); - if (srd_session_send(session, i, chunk_end, chunk, + if (srd_session_send(session, chunk_type, i, chunk_end, chunk, (chunk_end - i) * unit_size, unit_size) != SRD_OK) { _error_message = tr("Decoder reported an error"); break; } + if (logic_di && logic_di->logic_mask != 0) { + uint64_t cur_pos = logic_di->cur_pos; + uint64_t sample = _snapshot->get_sample(cur_pos) & logic_di->logic_mask; + if (logic_di->edge_index == -1) { + std::vector pos_vector; + cur_pos++; + for (int j =0 ; j < logic_di->dec_num_channels; j++) { + int index = logic_di->dec_channelmap[j]; + if (index != -1 && (logic_di->logic_mask & (1 << index))) { + bool last_sample = (sample & (1 << index)) ? 1 : 0; + pos_vector.push_back(cur_pos); + _snapshot->get_nxt_edge(pos_vector.back(), last_sample, decode_end, 1, index); + } + } + cur_pos = *std::min_element(pos_vector.begin(), pos_vector.end()); + } else { + bool last_sample = (sample & (1 << logic_di->edge_index)) ? 1 : 0; + do { + cur_pos++; + if (!_snapshot->get_nxt_edge(cur_pos, last_sample, decode_end, 1, logic_di->edge_index)) + break; + sample = _snapshot->get_sample(cur_pos) & logic_di->logic_mask; + last_sample = (sample & (1 << logic_di->edge_index)) ? 1 : 0; + } while(sample != logic_di->exp_logic); + } + + i = cur_pos; + if (i >= decode_end) + i = decode_end; + chunk_type = 0; + } else { + i += chunk_sample_count; + chunk_type = 1; + } + { lock_guard lock(_output_mutex); - _samples_decoded = chunk_end - decode_start + 1; + _samples_decoded = i - decode_start + 1; } if ((i - last_cnt) > notify_cnt) { last_cnt = i; new_decode_data(); } - } _options_changed = false; decode_done(); diff --git a/DSView/pv/data/decoderstack.h b/DSView/pv/data/decoderstack.h index 40be16db..143ad01d 100644 --- a/DSView/pv/data/decoderstack.h +++ b/DSView/pv/data/decoderstack.h @@ -20,8 +20,7 @@ #ifndef DSVIEW_PV_DATA_DECODERSTACK_H #define DSVIEW_PV_DATA_DECODERSTACK_H - -#include "signaldata.h" +#include #include @@ -32,14 +31,9 @@ #include #include -#include -#include - -struct srd_decoder; -struct srd_decoder_annotation_row; -struct srd_channel; -struct srd_proto_data; -struct srd_session; +#include <../data/decode/row.h> +#include <../data/decode/rowdata.h> +#include <../data/signaldata.h> namespace DecoderStackTest { class TwoDecoderStack; diff --git a/DSView/pv/data/logicsnapshot.cpp b/DSView/pv/data/logicsnapshot.cpp index df77821f..edea2467 100644 --- a/DSView/pv/data/logicsnapshot.cpp +++ b/DSView/pv/data/logicsnapshot.cpp @@ -23,6 +23,8 @@ #include +#include + #include #include #include @@ -208,25 +210,25 @@ void LogicSnapshot::get_subsampled_edges( uint64_t start, uint64_t end, float min_length, int sig_index) { - uint64_t index = start; - bool last_sample; + if (!edges.empty()) + edges.clear(); - assert(end <= get_sample_count()); + if (_sample_count == 0) + return; + + assert(end < _sample_count); assert(start <= end); assert(min_length > 0); assert(sig_index >= 0); assert(sig_index < 64); - if (_data.size() == 0) - return; - boost::lock_guard lock(_mutex); - + uint64_t index = start; + bool last_sample; const uint64_t block_length = (uint64_t)max(min_length, 1.0f); const uint64_t sig_mask = 1ULL << sig_index; - if (!edges.empty()) - edges.clear(); + // Store the initial state last_sample = (get_sample(start) & sig_mask) != 0; edges.push_back(pair(index++, last_sample)); diff --git a/DSView/pv/data/snapshot.cpp b/DSView/pv/data/snapshot.cpp index e5e9e741..c68d03ae 100644 --- a/DSView/pv/data/snapshot.cpp +++ b/DSView/pv/data/snapshot.cpp @@ -23,6 +23,8 @@ #include "snapshot.h" +#include + #include #include #include diff --git a/DSView/pv/dock/dsotriggerdock.cpp b/DSView/pv/dock/dsotriggerdock.cpp index f825695d..a6d99456 100644 --- a/DSView/pv/dock/dsotriggerdock.cpp +++ b/DSView/pv/dock/dsotriggerdock.cpp @@ -155,10 +155,10 @@ DsoTriggerDock::~DsoTriggerDock() void DsoTriggerDock::paintEvent(QPaintEvent *) { - QStyleOption opt; - opt.init(this); - QPainter p(this); - style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); +// QStyleOption opt; +// opt.init(this); +// QPainter p(this); +// style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); } void DsoTriggerDock::pos_changed(int pos) diff --git a/DSView/pv/dock/measuredock.cpp b/DSView/pv/dock/measuredock.cpp index c49f0743..67a7e2fe 100644 --- a/DSView/pv/dock/measuredock.cpp +++ b/DSView/pv/dock/measuredock.cpp @@ -147,10 +147,10 @@ MeasureDock::~MeasureDock() void MeasureDock::paintEvent(QPaintEvent *) { - QStyleOption opt; - opt.init(this); - QPainter p(this); - style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); +// QStyleOption opt; +// opt.init(this); +// QPainter p(this); +// style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); } void MeasureDock::cursor_update() diff --git a/DSView/pv/dock/searchdock.cpp b/DSView/pv/dock/searchdock.cpp index 17fe110c..90b1bbab 100644 --- a/DSView/pv/dock/searchdock.cpp +++ b/DSView/pv/dock/searchdock.cpp @@ -102,10 +102,10 @@ SearchDock::~SearchDock() void SearchDock::paintEvent(QPaintEvent *) { - QStyleOption opt; - opt.init(this); - QPainter p(this); - style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); +// QStyleOption opt; +// opt.init(this); +// QPainter p(this); +// style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); } void SearchDock::on_previous() diff --git a/DSView/pv/dock/triggerdock.cpp b/DSView/pv/dock/triggerdock.cpp index 6dc4ca6d..a16eb34e 100644 --- a/DSView/pv/dock/triggerdock.cpp +++ b/DSView/pv/dock/triggerdock.cpp @@ -271,10 +271,10 @@ TriggerDock::~TriggerDock() void TriggerDock::paintEvent(QPaintEvent *) { - QStyleOption opt; - opt.init(this); - QPainter p(this); - style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); +// QStyleOption opt; +// opt.init(this); +// QPainter p(this); +// style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); } void TriggerDock::simple_trigger() diff --git a/DSView/pv/mainwindow.cpp b/DSView/pv/mainwindow.cpp index 57ca57f2..42e81399 100644 --- a/DSView/pv/mainwindow.cpp +++ b/DSView/pv/mainwindow.cpp @@ -112,7 +112,7 @@ void MainWindow::setup_ui() { setObjectName(QString::fromUtf8("MainWindow")); setMinimumHeight(680); - setMinimumWidth(500); + setMinimumWidth(800); resize(1024, 768); // Set the window icon diff --git a/DSView/pv/view/decodetrace.cpp b/DSView/pv/view/decodetrace.cpp index b47de76c..e16c0f8b 100644 --- a/DSView/pv/view/decodetrace.cpp +++ b/DSView/pv/view/decodetrace.cpp @@ -197,6 +197,41 @@ void DecodeTrace::paint_back(QPainter &p, int left, int right) p.drawPolygon(start_points, countof(start_points)); p.drawPolygon(end_points, countof(end_points)); + // --draw headings + const int row_height = _view->get_signalHeight(); + for (size_t i = 0; i < _cur_row_headings.size(); i++) + { + const int y = i * row_height + get_y() - _totalHeight * 0.5; + + p.setPen(QPen(Qt::NoPen)); + p.setBrush(QApplication::palette().brush(QPalette::WindowText)); + + const QRect r(left + ArrowSize * 2, y, + right - left, row_height / 2); + const QString h(_cur_row_headings[i]); + const int f = Qt::AlignLeft | Qt::AlignVCenter | + Qt::TextDontClip; + const QPointF points[] = { + QPointF(left, r.center().y() - ArrowSize), + QPointF(left + ArrowSize, r.center().y()), + QPointF(left, r.center().y() + ArrowSize) + }; + p.drawPolygon(points, countof(points)); + + // Draw the outline + QFont font=p.font(); + font.setPointSize(DefaultFontSize); + p.setFont(font); +// p.setPen(QApplication::palette().color(QPalette::Base)); +// for (int dx = -1; dx <= 1; dx++) +// for (int dy = -1; dy <= 1; dy++) +// if (dx != 0 && dy != 0) +// p.drawText(r.translated(dx, dy), f, h); + + // Draw the text + p.setPen(DARK_FORE); + p.drawText(r, f, h); + } } void DecodeTrace::paint_mid(QPainter &p, int left, int right) @@ -300,45 +335,45 @@ void DecodeTrace::paint_fore(QPainter &p, int left, int right) (void)right; - const int row_height = _view->get_signalHeight(); +// const int row_height = _view->get_signalHeight(); - for (size_t i = 0; i < _cur_row_headings.size(); i++) - { - const int y = (i + 0.5) * row_height + get_y() - _totalHeight * 0.5; +// for (size_t i = 0; i < _cur_row_headings.size(); i++) +// { +// const int y = (i + 0.5) * row_height + get_y() - _totalHeight * 0.5; - p.setPen(QPen(Qt::NoPen)); - p.setBrush(QApplication::palette().brush(QPalette::WindowText)); +// p.setPen(QPen(Qt::NoPen)); +// p.setBrush(QApplication::palette().brush(QPalette::WindowText)); - if (i != 0) - { - const QPointF points[] = { - QPointF(left, y - ArrowSize), - QPointF(left + ArrowSize, y), - QPointF(left, y + ArrowSize) - }; - p.drawPolygon(points, countof(points)); - } +// if (i != 0) +// { +// const QPointF points[] = { +// QPointF(left, y - ArrowSize), +// QPointF(left + ArrowSize, y), +// QPointF(left, y + ArrowSize) +// }; +// p.drawPolygon(points, countof(points)); +// } - const QRect r(left + ArrowSize * 2, y - row_height / 2, - right - left, row_height); - const QString h(_cur_row_headings[i]); - const int f = Qt::AlignLeft | Qt::AlignBottom | - Qt::TextDontClip; +// const QRect r(left + ArrowSize * 2, y - row_height / 2, +// right - left, row_height); +// const QString h(_cur_row_headings[i]); +// const int f = Qt::AlignLeft | Qt::AlignBottom | +// Qt::TextDontClip; - // Draw the outline - QFont font=p.font(); - font.setPointSize(DefaultFontSize); - p.setFont(font); - p.setPen(QApplication::palette().color(QPalette::Base)); - for (int dx = -1; dx <= 1; dx++) - for (int dy = -1; dy <= 1; dy++) - if (dx != 0 && dy != 0) - p.drawText(r.translated(dx, dy), f, h); +// // Draw the outline +// QFont font=p.font(); +// font.setPointSize(DefaultFontSize); +// p.setFont(font); +//// p.setPen(QApplication::palette().color(QPalette::Base)); +//// for (int dx = -1; dx <= 1; dx++) +//// for (int dy = -1; dy <= 1; dy++) +//// if (dx != 0 && dy != 0) +//// p.drawText(r.translated(dx, dy), f, h); - // Draw the text - p.setPen(QApplication::palette().color(QPalette::WindowText)); - p.drawText(r, f, h); - } +// // Draw the text +// p.setPen(DARK_FORE); +// p.drawText(r, f, h); +// } } bool DecodeTrace::create_popup() @@ -506,7 +541,7 @@ void DecodeTrace::draw_nodetail(QPainter &p, size_t base_colour) const { const QRectF nodetail_rect(left, y - h/2 + 0.5, right - left, h); - QString info = tr("Zoom in For Detial"); + QString info = tr("Zoom in For Detials"); int info_left = nodetail_rect.center().x() - p.boundingRect(QRectF(), 0, info).width(); int info_right = nodetail_rect.center().x() + p.boundingRect(QRectF(), 0, info).width(); int height = p.boundingRect(QRectF(), 0, info).height(); diff --git a/DSView/pv/view/header.cpp b/DSView/pv/view/header.cpp index a490805b..2eb1d5fb 100644 --- a/DSView/pv/view/header.cpp +++ b/DSView/pv/view/header.cpp @@ -119,7 +119,7 @@ void Header::paintEvent(QPaintEvent*) //painter.setRenderHint(QPainter::Antialiasing); style()->drawPrimitive(QStyle::PE_Widget, &o, &painter, this); - painter.begin(this); + //painter.begin(this); const int w = width(); const vector< boost::shared_ptr > traces( diff --git a/DSView/pv/view/logicsignal.cpp b/DSView/pv/view/logicsignal.cpp index fb911fd7..09a43232 100644 --- a/DSView/pv/view/logicsignal.cpp +++ b/DSView/pv/view/logicsignal.cpp @@ -23,6 +23,8 @@ #include +#include + #include #include "logicsignal.h" diff --git a/DSView/pv/view/ruler.cpp b/DSView/pv/view/ruler.cpp index 171d99de..209a75a1 100644 --- a/DSView/pv/view/ruler.cpp +++ b/DSView/pv/view/ruler.cpp @@ -182,7 +182,7 @@ void Ruler::paintEvent(QPaintEvent*) QPainter p(this); style()->drawPrimitive(QStyle::PE_Widget, &o, &p, this); - p.begin(this); + //p.begin(this); //QPainter p(this); //p.setRenderHint(QPainter::Antialiasing); diff --git a/DSView/pv/view/viewport.cpp b/DSView/pv/view/viewport.cpp index 7d5c87f7..9197f0ce 100644 --- a/DSView/pv/view/viewport.cpp +++ b/DSView/pv/view/viewport.cpp @@ -128,7 +128,7 @@ void Viewport::paintEvent(QPaintEvent *event) QPainter p(this); style()->drawPrimitive(QStyle::PE_Widget, &o, &p, this); - p.begin(this); + //p.begin(this); const vector< boost::shared_ptr > traces(_view.get_traces(_type)); BOOST_FOREACH(const boost::shared_ptr t, traces) { From 57188cf7adde5ccd952e60c1e58c06517308eed4 Mon Sep 17 00:00:00 2001 From: DreamSourceLab Date: Wed, 8 Jun 2016 12:18:50 +0800 Subject: [PATCH 20/32] Add DSCope20 hardware support --- DSView/dsapplication.cpp | 2 +- DSView/dsapplication.h | 2 +- DSView/extdef.h | 2 +- DSView/pv/data/analog.cpp | 2 +- DSView/pv/data/analog.h | 2 +- DSView/pv/data/analogsnapshot.cpp | 2 +- DSView/pv/data/analogsnapshot.h | 2 +- DSView/pv/data/decode/annotation.cpp | 1 + DSView/pv/data/decode/annotation.h | 1 + DSView/pv/data/decode/decoder.cpp | 1 + DSView/pv/data/decode/decoder.h | 1 + DSView/pv/data/decode/rowdata.cpp | 1 + DSView/pv/data/decode/rowdata.h | 1 + DSView/pv/data/decodermodel.cpp | 2 +- DSView/pv/data/decodermodel.h | 2 +- DSView/pv/data/decoderstack.cpp | 1 + DSView/pv/data/decoderstack.h | 1 + DSView/pv/data/dso.cpp | 2 +- DSView/pv/data/dso.h | 2 +- DSView/pv/data/dsosnapshot.cpp | 2 +- DSView/pv/data/dsosnapshot.h | 2 +- DSView/pv/data/group.cpp | 3 +- DSView/pv/data/group.h | 3 +- DSView/pv/data/groupsnapshot.cpp | 3 +- DSView/pv/data/groupsnapshot.h | 3 +- DSView/pv/data/logic.cpp | 2 +- DSView/pv/data/logic.h | 2 +- DSView/pv/data/logicsnapshot.cpp | 2 +- DSView/pv/data/logicsnapshot.h | 2 +- DSView/pv/data/mathstack.cpp | 2 +- DSView/pv/data/mathstack.h | 2 +- DSView/pv/data/signaldata.cpp | 2 +- DSView/pv/data/signaldata.h | 2 +- DSView/pv/data/snapshot.cpp | 2 +- DSView/pv/data/snapshot.h | 2 +- DSView/pv/devicemanager.cpp | 2 +- DSView/pv/devicemanager.h | 2 +- DSView/pv/dialogs/about.cpp | 2 +- DSView/pv/dialogs/about.h | 2 +- DSView/pv/dialogs/calibration.cpp | 302 +++++ DSView/pv/dialogs/calibration.h | 77 ++ DSView/pv/dialogs/deviceoptions.cpp | 17 +- DSView/pv/dialogs/deviceoptions.h | 4 +- DSView/pv/dialogs/dsomeasure.cpp | 3 +- DSView/pv/dialogs/dsomeasure.h | 3 +- DSView/pv/dialogs/fftoptions.cpp | 3 +- DSView/pv/dialogs/fftoptions.h | 3 +- DSView/pv/dialogs/protocolexp.cpp | 3 +- DSView/pv/dialogs/protocolexp.h | 3 +- DSView/pv/dialogs/protocollist.cpp | 3 +- DSView/pv/dialogs/protocollist.h | 3 +- DSView/pv/dialogs/search.cpp | 3 +- DSView/pv/dialogs/search.h | 3 +- DSView/pv/dialogs/streamoptions.cpp | 3 +- DSView/pv/dialogs/streamoptions.h | 3 +- DSView/pv/dialogs/waitingdialog.cpp | 78 +- DSView/pv/dialogs/waitingdialog.h | 6 +- DSView/pv/dock/dsotriggerdock.cpp | 3 +- DSView/pv/dock/dsotriggerdock.h | 3 +- DSView/pv/dock/measuredock.cpp | 3 +- DSView/pv/dock/measuredock.h | 3 +- DSView/pv/dock/protocoldock.cpp | 3 +- DSView/pv/dock/protocoldock.h | 3 +- DSView/pv/dock/searchdock.cpp | 3 +- DSView/pv/dock/searchdock.h | 3 +- DSView/pv/dock/triggerdock.cpp | 3 +- DSView/pv/dock/triggerdock.h | 3 +- DSView/pv/mainwindow.cpp | 26 +- DSView/pv/mainwindow.h | 6 +- DSView/pv/prop/binding/binding.cpp | 2 +- DSView/pv/prop/binding/binding.h | 2 +- .../pv/prop/binding/binding_deviceoptions.cpp | 2 +- DSView/pv/prop/binding/deviceoptions.h | 2 +- DSView/pv/prop/bool.cpp | 1 - DSView/pv/prop/bool.h | 1 - DSView/pv/prop/double.cpp | 1 - DSView/pv/prop/double.h | 1 - DSView/pv/prop/enum.cpp | 1 - DSView/pv/prop/enum.h | 1 - DSView/pv/prop/int.cpp | 1 - DSView/pv/prop/int.h | 1 - DSView/pv/prop/property.cpp | 1 - DSView/pv/prop/property.h | 1 - DSView/pv/sigsession.cpp | 2 +- DSView/pv/sigsession.h | 2 +- DSView/pv/toolbars/filebar.cpp | 3 +- DSView/pv/toolbars/filebar.h | 3 +- DSView/pv/toolbars/logobar.cpp | 3 +- DSView/pv/toolbars/logobar.h | 3 +- DSView/pv/toolbars/samplingbar.cpp | 79 +- DSView/pv/toolbars/samplingbar.h | 6 +- DSView/pv/toolbars/trigbar.cpp | 3 +- DSView/pv/toolbars/trigbar.h | 3 +- DSView/pv/view/analogsignal.cpp | 2 +- DSView/pv/view/analogsignal.h | 3 +- DSView/pv/view/cursor.cpp | 2 +- DSView/pv/view/cursor.h | 2 +- DSView/pv/view/decodetrace.cpp | 1 + DSView/pv/view/decodetrace.h | 1 + DSView/pv/view/devmode.cpp | 2 +- DSView/pv/view/devmode.h | 2 +- DSView/pv/view/dsldial.cpp | 21 + DSView/pv/view/dsldial.h | 21 + DSView/pv/view/dsosignal.cpp | 81 +- DSView/pv/view/dsosignal.h | 3 +- DSView/pv/view/groupsignal.cpp | 3 +- DSView/pv/view/groupsignal.h | 3 +- DSView/pv/view/header.cpp | 2 +- DSView/pv/view/header.h | 2 +- DSView/pv/view/logicsignal.cpp | 2 +- DSView/pv/view/logicsignal.h | 2 +- DSView/pv/view/mathtrace.cpp | 2 +- DSView/pv/view/mathtrace.h | 2 +- DSView/pv/view/ruler.cpp | 2 +- DSView/pv/view/ruler.h | 2 +- DSView/pv/view/selectableitem.cpp | 2 +- DSView/pv/view/selectableitem.h | 2 +- DSView/pv/view/signal.cpp | 2 +- DSView/pv/view/signal.h | 2 +- DSView/pv/view/timemarker.cpp | 2 +- DSView/pv/view/timemarker.h | 2 +- DSView/pv/view/trace.cpp | 2 +- DSView/pv/view/trace.h | 2 +- DSView/pv/view/view.cpp | 25 +- DSView/pv/view/view.h | 9 +- DSView/pv/view/viewport.cpp | 2 +- DSView/pv/view/viewport.h | 2 +- DSView/pv/widgets/decodergroupbox.cpp | 1 + DSView/pv/widgets/decodergroupbox.h | 1 + DSView/pv/widgets/fakelineedit.cpp | 3 +- DSView/pv/widgets/fakelineedit.h | 3 +- libsigrok4DSL/hardware/DSL/command.c | 14 +- libsigrok4DSL/hardware/DSL/command.h | 52 +- libsigrok4DSL/hardware/DSL/dscope.c | 1160 ++++++++++++----- libsigrok4DSL/hardware/DSL/dsl.h | 65 +- libsigrok4DSL/hardware/DSL/dslogic.c | 67 +- libsigrok4DSL/hardware/demo/demo.c | 1 + libsigrok4DSL/hwdriver.c | 1 - libsigrok4DSL/libsigrok.h | 31 +- 139 files changed, 1710 insertions(+), 687 deletions(-) create mode 100755 DSView/pv/dialogs/calibration.cpp create mode 100755 DSView/pv/dialogs/calibration.h diff --git a/DSView/dsapplication.cpp b/DSView/dsapplication.cpp index 1a8df330..6ac6f80d 100644 --- a/DSView/dsapplication.cpp +++ b/DSView/dsapplication.cpp @@ -2,7 +2,7 @@ * This file is part of the DSView project. * DSView is based on PulseView. * - * Copyright (C) 2016 DreamSourceLab + * Copyright (C) 2016 DreamSourceLab * * 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 diff --git a/DSView/dsapplication.h b/DSView/dsapplication.h index 7e0b3922..7e804745 100644 --- a/DSView/dsapplication.h +++ b/DSView/dsapplication.h @@ -2,7 +2,7 @@ * This file is part of the DSView project. * DSView is based on PulseView. * - * Copyright (C) 2016 DreamSourceLab + * Copyright (C) 2016 DreamSourceLab * * 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 diff --git a/DSView/extdef.h b/DSView/extdef.h index 192bb8f3..7fc608fe 100644 --- a/DSView/extdef.h +++ b/DSView/extdef.h @@ -3,7 +3,7 @@ * DSView is based on PulseView. * * Copyright (C) 2012 Joel Holdsworth - * Copyright (C) 2013 DreamSourceLab + * Copyright (C) 2013 DreamSourceLab * * 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 diff --git a/DSView/pv/data/analog.cpp b/DSView/pv/data/analog.cpp index e5c85b39..790c12a5 100644 --- a/DSView/pv/data/analog.cpp +++ b/DSView/pv/data/analog.cpp @@ -3,7 +3,7 @@ * DSView is based on PulseView. * * Copyright (C) 2012 Joel Holdsworth - * Copyright (C) 2013 DreamSourceLab + * Copyright (C) 2013 DreamSourceLab * * 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 diff --git a/DSView/pv/data/analog.h b/DSView/pv/data/analog.h index b53aff6e..dc2a8571 100644 --- a/DSView/pv/data/analog.h +++ b/DSView/pv/data/analog.h @@ -3,7 +3,7 @@ * DSView is based on PulseView. * * Copyright (C) 2012 Joel Holdsworth - * Copyright (C) 2013 DreamSourceLab + * Copyright (C) 2013 DreamSourceLab * * 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 diff --git a/DSView/pv/data/analogsnapshot.cpp b/DSView/pv/data/analogsnapshot.cpp index 66bf5833..4b8c1719 100644 --- a/DSView/pv/data/analogsnapshot.cpp +++ b/DSView/pv/data/analogsnapshot.cpp @@ -3,7 +3,7 @@ * DSView is based on PulseView. * * Copyright (C) 2012 Joel Holdsworth - * Copyright (C) 2013 DreamSourceLab + * Copyright (C) 2013 DreamSourceLab * * 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 diff --git a/DSView/pv/data/analogsnapshot.h b/DSView/pv/data/analogsnapshot.h index 7df1e111..b7a8813d 100644 --- a/DSView/pv/data/analogsnapshot.h +++ b/DSView/pv/data/analogsnapshot.h @@ -3,7 +3,7 @@ * DSView is based on PulseView. * * Copyright (C) 2012 Joel Holdsworth - * Copyright (C) 2013 DreamSourceLab + * Copyright (C) 2013 DreamSourceLab * * 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 diff --git a/DSView/pv/data/decode/annotation.cpp b/DSView/pv/data/decode/annotation.cpp index 652dc1b9..1cf20090 100644 --- a/DSView/pv/data/decode/annotation.cpp +++ b/DSView/pv/data/decode/annotation.cpp @@ -2,6 +2,7 @@ * This file is part of the PulseView project. * * Copyright (C) 2012 Joel Holdsworth + * Copyright (C) 2016 DreamSourceLab * * 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 diff --git a/DSView/pv/data/decode/annotation.h b/DSView/pv/data/decode/annotation.h index 9ed39bf9..cba04c99 100644 --- a/DSView/pv/data/decode/annotation.h +++ b/DSView/pv/data/decode/annotation.h @@ -2,6 +2,7 @@ * This file is part of the PulseView project. * * Copyright (C) 2013 Joel Holdsworth + * Copyright (C) 2016 DreamSourceLab * * 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 diff --git a/DSView/pv/data/decode/decoder.cpp b/DSView/pv/data/decode/decoder.cpp index 5c27c085..8d54d7f6 100644 --- a/DSView/pv/data/decode/decoder.cpp +++ b/DSView/pv/data/decode/decoder.cpp @@ -2,6 +2,7 @@ * This file is part of the PulseView project. * * Copyright (C) 2013 Joel Holdsworth + * Copyright (C) 2014 DreamSourceLab * * 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 diff --git a/DSView/pv/data/decode/decoder.h b/DSView/pv/data/decode/decoder.h index 607bc0b8..40fde4e6 100644 --- a/DSView/pv/data/decode/decoder.h +++ b/DSView/pv/data/decode/decoder.h @@ -2,6 +2,7 @@ * This file is part of the PulseView project. * * Copyright (C) 2013 Joel Holdsworth + * Copyright (C) 2014 DreamSourceLab * * 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 diff --git a/DSView/pv/data/decode/rowdata.cpp b/DSView/pv/data/decode/rowdata.cpp index 1fbd31a5..8d3e41e0 100644 --- a/DSView/pv/data/decode/rowdata.cpp +++ b/DSView/pv/data/decode/rowdata.cpp @@ -2,6 +2,7 @@ * This file is part of the PulseView project. * * Copyright (C) 2014 Joel Holdsworth + * Copyright (C) 2016 DreamSourceLab * * 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 diff --git a/DSView/pv/data/decode/rowdata.h b/DSView/pv/data/decode/rowdata.h index 11b88420..e2a84f31 100644 --- a/DSView/pv/data/decode/rowdata.h +++ b/DSView/pv/data/decode/rowdata.h @@ -2,6 +2,7 @@ * This file is part of the PulseView project. * * Copyright (C) 2014 Joel Holdsworth + * Copyright (C) 2016 DreamSourceLab * * 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 diff --git a/DSView/pv/data/decodermodel.cpp b/DSView/pv/data/decodermodel.cpp index 9e2c071f..078111a4 100644 --- a/DSView/pv/data/decodermodel.cpp +++ b/DSView/pv/data/decodermodel.cpp @@ -1,7 +1,7 @@ /* * This file is part of the DSView project. * - * Copyright (C) 2016 Andy Deng + * Copyright (C) 2016 DreamSourceLab * * 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 diff --git a/DSView/pv/data/decodermodel.h b/DSView/pv/data/decodermodel.h index a0eac14b..4616e250 100644 --- a/DSView/pv/data/decodermodel.h +++ b/DSView/pv/data/decodermodel.h @@ -1,7 +1,7 @@ /* * This file is part of the DSView project. * - * Copyright (C) 2016 Andy Deng + * Copyright (C) 2016 DreamSourceLab * * 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 diff --git a/DSView/pv/data/decoderstack.cpp b/DSView/pv/data/decoderstack.cpp index 7615e0e2..c480a006 100644 --- a/DSView/pv/data/decoderstack.cpp +++ b/DSView/pv/data/decoderstack.cpp @@ -2,6 +2,7 @@ * This file is part of the PulseView project. * * Copyright (C) 2012 Joel Holdsworth + * Copyright (C) 2014 DreamSourceLab * * 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 diff --git a/DSView/pv/data/decoderstack.h b/DSView/pv/data/decoderstack.h index 143ad01d..7516c907 100644 --- a/DSView/pv/data/decoderstack.h +++ b/DSView/pv/data/decoderstack.h @@ -2,6 +2,7 @@ * This file is part of the PulseView project. * * Copyright (C) 2012 Joel Holdsworth + * Copyright (C) 2014 DreamSourceLab * * 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 diff --git a/DSView/pv/data/dso.cpp b/DSView/pv/data/dso.cpp index fa4cf4a5..fe3ce699 100644 --- a/DSView/pv/data/dso.cpp +++ b/DSView/pv/data/dso.cpp @@ -2,7 +2,7 @@ * This file is part of the DSView project. * DSView is based on PulseView. * - * Copyright (C) 2013 DreamSourceLab + * Copyright (C) 2013 DreamSourceLab * * 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 diff --git a/DSView/pv/data/dso.h b/DSView/pv/data/dso.h index be987821..0e724ed6 100644 --- a/DSView/pv/data/dso.h +++ b/DSView/pv/data/dso.h @@ -2,7 +2,7 @@ * This file is part of the DSView project. * DSView is based on PulseView. * - * Copyright (C) 2013 DreamSourceLab + * Copyright (C) 2013 DreamSourceLab * * 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 diff --git a/DSView/pv/data/dsosnapshot.cpp b/DSView/pv/data/dsosnapshot.cpp index 7fdd78f8..b4f93c91 100644 --- a/DSView/pv/data/dsosnapshot.cpp +++ b/DSView/pv/data/dsosnapshot.cpp @@ -2,7 +2,7 @@ * This file is part of the DSView project. * DSView is based on PulseView. * - * Copyright (C) 2013 DreamSourceLab + * Copyright (C) 2013 DreamSourceLab * * 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 diff --git a/DSView/pv/data/dsosnapshot.h b/DSView/pv/data/dsosnapshot.h index fbbdf849..e1488374 100644 --- a/DSView/pv/data/dsosnapshot.h +++ b/DSView/pv/data/dsosnapshot.h @@ -2,7 +2,7 @@ * This file is part of the DSView project. * DSView is based on PulseView. * - * Copyright (C) 2013 DreamSourceLab + * Copyright (C) 2013 DreamSourceLab * * 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 diff --git a/DSView/pv/data/group.cpp b/DSView/pv/data/group.cpp index e0aab57f..3d086e08 100644 --- a/DSView/pv/data/group.cpp +++ b/DSView/pv/data/group.cpp @@ -2,8 +2,7 @@ * This file is part of the DSView project. * DSView is based on PulseView. * - * Copyright (C) 2012 Joel Holdsworth - * Copyright (C) 2013 DreamSourceLab + * Copyright (C) 2013 DreamSourceLab * * 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 diff --git a/DSView/pv/data/group.h b/DSView/pv/data/group.h index 25f9fb29..f166bf94 100644 --- a/DSView/pv/data/group.h +++ b/DSView/pv/data/group.h @@ -2,8 +2,7 @@ * This file is part of the DSView project. * DSView is based on PulseView. * - * Copyright (C) 2012 Joel Holdsworth - * Copyright (C) 2013 DreamSourceLab + * Copyright (C) 2013 DreamSourceLab * * 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 diff --git a/DSView/pv/data/groupsnapshot.cpp b/DSView/pv/data/groupsnapshot.cpp index 69a18f1a..6f0026dd 100644 --- a/DSView/pv/data/groupsnapshot.cpp +++ b/DSView/pv/data/groupsnapshot.cpp @@ -2,8 +2,7 @@ * This file is part of the DSView project. * DSView is based on PulseView. * - * Copyright (C) 2012 Joel Holdsworth - * Copyright (C) 2013 DreamSourceLab + * Copyright (C) 2013 DreamSourceLab * * 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 diff --git a/DSView/pv/data/groupsnapshot.h b/DSView/pv/data/groupsnapshot.h index ed475b3a..6ad65522 100644 --- a/DSView/pv/data/groupsnapshot.h +++ b/DSView/pv/data/groupsnapshot.h @@ -2,8 +2,7 @@ * This file is part of the DSView project. * DSView is based on PulseView. * - * Copyright (C) 2012 Joel Holdsworth - * Copyright (C) 2013 DreamSourceLab + * Copyright (C) 2013 DreamSourceLab * * 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 diff --git a/DSView/pv/data/logic.cpp b/DSView/pv/data/logic.cpp index a249eda8..f2a7cee4 100644 --- a/DSView/pv/data/logic.cpp +++ b/DSView/pv/data/logic.cpp @@ -3,7 +3,7 @@ * DSView is based on PulseView. * * Copyright (C) 2012 Joel Holdsworth - * Copyright (C) 2013 DreamSourceLab + * Copyright (C) 2013 DreamSourceLab * * 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 diff --git a/DSView/pv/data/logic.h b/DSView/pv/data/logic.h index c7a97158..fe815264 100644 --- a/DSView/pv/data/logic.h +++ b/DSView/pv/data/logic.h @@ -3,7 +3,7 @@ * DSView is based on PulseView. * * Copyright (C) 2012 Joel Holdsworth - * Copyright (C) 2013 DreamSourceLab + * Copyright (C) 2013 DreamSourceLab * * 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 diff --git a/DSView/pv/data/logicsnapshot.cpp b/DSView/pv/data/logicsnapshot.cpp index edea2467..17da83c7 100644 --- a/DSView/pv/data/logicsnapshot.cpp +++ b/DSView/pv/data/logicsnapshot.cpp @@ -3,7 +3,7 @@ * DSView is based on PulseView. * * Copyright (C) 2012 Joel Holdsworth - * Copyright (C) 2013 DreamSourceLab + * Copyright (C) 2013 DreamSourceLab * * 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 diff --git a/DSView/pv/data/logicsnapshot.h b/DSView/pv/data/logicsnapshot.h index ecaab06b..475d8bf0 100644 --- a/DSView/pv/data/logicsnapshot.h +++ b/DSView/pv/data/logicsnapshot.h @@ -3,7 +3,7 @@ * DSView is based on PulseView. * * Copyright (C) 2012 Joel Holdsworth - * Copyright (C) 2013 DreamSourceLab + * Copyright (C) 2013 DreamSourceLab * * 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 diff --git a/DSView/pv/data/mathstack.cpp b/DSView/pv/data/mathstack.cpp index 6494351c..13aa18d6 100644 --- a/DSView/pv/data/mathstack.cpp +++ b/DSView/pv/data/mathstack.cpp @@ -1,7 +1,7 @@ /* * This file is part of the PulseView project. * - * Copyright (C) 2012 Joel Holdsworth + * Copyright (C) 2016 DreamSourceLab * * 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 diff --git a/DSView/pv/data/mathstack.h b/DSView/pv/data/mathstack.h index dbbe21bc..6616ac44 100644 --- a/DSView/pv/data/mathstack.h +++ b/DSView/pv/data/mathstack.h @@ -1,7 +1,7 @@ /* * This file is part of the PulseView project. * - * Copyright (C) 2012 Joel Holdsworth + * Copyright (C) 2016 DreamSourceLab * * 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 diff --git a/DSView/pv/data/signaldata.cpp b/DSView/pv/data/signaldata.cpp index b97e026a..afb7f240 100644 --- a/DSView/pv/data/signaldata.cpp +++ b/DSView/pv/data/signaldata.cpp @@ -3,7 +3,7 @@ * DSView is based on PulseView. * * Copyright (C) 2012 Joel Holdsworth - * Copyright (C) 2013 DreamSourceLab + * Copyright (C) 2013 DreamSourceLab * * 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 diff --git a/DSView/pv/data/signaldata.h b/DSView/pv/data/signaldata.h index 86279376..464215da 100644 --- a/DSView/pv/data/signaldata.h +++ b/DSView/pv/data/signaldata.h @@ -3,7 +3,7 @@ * DSView is based on PulseView. * * Copyright (C) 2012 Joel Holdsworth - * Copyright (C) 2013 DreamSourceLab + * Copyright (C) 2013 DreamSourceLab * * 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 diff --git a/DSView/pv/data/snapshot.cpp b/DSView/pv/data/snapshot.cpp index c68d03ae..37eb9acf 100644 --- a/DSView/pv/data/snapshot.cpp +++ b/DSView/pv/data/snapshot.cpp @@ -3,7 +3,7 @@ * DSView is based on PulseView. * * Copyright (C) 2012 Joel Holdsworth - * Copyright (C) 2013 DreamSourceLab + * Copyright (C) 2013 DreamSourceLab * * 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 diff --git a/DSView/pv/data/snapshot.h b/DSView/pv/data/snapshot.h index 9f2be22c..ca0e8831 100644 --- a/DSView/pv/data/snapshot.h +++ b/DSView/pv/data/snapshot.h @@ -3,7 +3,7 @@ * DSView is based on PulseView. * * Copyright (C) 2012 Joel Holdsworth - * Copyright (C) 2013 DreamSourceLab + * Copyright (C) 2013 DreamSourceLab * * 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 diff --git a/DSView/pv/devicemanager.cpp b/DSView/pv/devicemanager.cpp index 066af640..474db42b 100644 --- a/DSView/pv/devicemanager.cpp +++ b/DSView/pv/devicemanager.cpp @@ -3,7 +3,7 @@ * DSView is based on PulseView. * * Copyright (C) 2012 Joel Holdsworth - * Copyright (C) 2013 DreamSourceLab + * Copyright (C) 2013 DreamSourceLab * * 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 diff --git a/DSView/pv/devicemanager.h b/DSView/pv/devicemanager.h index fde8ad1e..3f0b313a 100644 --- a/DSView/pv/devicemanager.h +++ b/DSView/pv/devicemanager.h @@ -3,7 +3,7 @@ * DSView is based on PulseView. * * Copyright (C) 2012 Joel Holdsworth - * Copyright (C) 2013 DreamSourceLab + * Copyright (C) 2013 DreamSourceLab * * 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 diff --git a/DSView/pv/dialogs/about.cpp b/DSView/pv/dialogs/about.cpp index 6809fd70..b402cb52 100644 --- a/DSView/pv/dialogs/about.cpp +++ b/DSView/pv/dialogs/about.cpp @@ -3,7 +3,7 @@ * DSView is based on PulseView. * * Copyright (C) 2012 Joel Holdsworth - * Copyright (C) 2013 DreamSourceLab + * Copyright (C) 2013 DreamSourceLab * * 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 diff --git a/DSView/pv/dialogs/about.h b/DSView/pv/dialogs/about.h index 6d140472..d31b7607 100644 --- a/DSView/pv/dialogs/about.h +++ b/DSView/pv/dialogs/about.h @@ -3,7 +3,7 @@ * DSView is based on PulseView. * * Copyright (C) 2012 Joel Holdsworth - * Copyright (C) 2013 DreamSourceLab + * Copyright (C) 2013 DreamSourceLab * * 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 diff --git a/DSView/pv/dialogs/calibration.cpp b/DSView/pv/dialogs/calibration.cpp new file mode 100755 index 00000000..fe2572aa --- /dev/null +++ b/DSView/pv/dialogs/calibration.cpp @@ -0,0 +1,302 @@ +/* + * This file is part of the DSView project. + * DSView is based on PulseView. + * + * Copyright (C) 2016 DreamSourceLab + * + * 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 "calibration.h" + +#include + +#include +#include +#include +#include +#include + +#include "libsigrok4DSL/libsigrok.h" +#include "../view/trace.h" + +using namespace boost; +using namespace std; + +namespace pv { +namespace dialogs { + +const QString Calibration::VGAIN = tr(" VGAIN"); +const QString Calibration::VOFF = tr(" VOFF"); + +Calibration::Calibration(QWidget *parent) : + QDialog(parent) +{ + this->setFixedSize(400, 200); + this->setWindowOpacity(0.7); + this->setWindowFlags(Qt::Dialog | Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint); + this->setModal(false); + + _dev_inst = NULL; + _save_btn = new QPushButton(tr("Save"), this); + _reset_btn = new QPushButton(tr("Reset"), this); + _exit_btn = new QPushButton(tr("Exit"), this); + + _flayout = new QFormLayout(); + QGridLayout *glayout = new QGridLayout(this); + glayout->addLayout(_flayout, 0, 0, 1, 5); + glayout->addWidget(_save_btn, 1, 0); + glayout->addWidget(new QWidget(this), 1, 1); + glayout->setColumnStretch(1, 1); + glayout->addWidget(_reset_btn, 1, 2); + glayout->addWidget(new QWidget(this), 1, 3); + glayout->setColumnStretch(3, 1); + glayout->addWidget(_exit_btn, 1, 4); + setLayout(glayout); + + connect(_save_btn, SIGNAL(clicked()), this, SLOT(on_save())); + connect(_reset_btn, SIGNAL(clicked()), this, SLOT(on_reset())); + connect(_exit_btn, SIGNAL(clicked()), this, SLOT(reject())); +} + +void Calibration::set_device(boost::shared_ptr dev_inst) +{ + assert(dev_inst); + _dev_inst = dev_inst; + + for(std::list::const_iterator i = _slider_list.begin(); + i != _slider_list.end(); i++) { + (*i)->setParent(NULL); + _flayout->removeWidget((*i)); + delete (*i); + } + _slider_list.clear(); + for(std::list::const_iterator i = _label_list.begin(); + i != _label_list.end(); i++) { + (*i)->setParent(NULL); + _flayout->removeWidget((*i)); + delete (*i); + } + _label_list.clear(); + + for (const GSList *l = _dev_inst->dev_inst()->channels; l; l = l->next) { + sr_channel *const probe = (sr_channel*)l->data; + assert(probe); + + uint64_t vgain, vgain_default; + uint16_t vgain_range; + GVariant* gvar = _dev_inst->get_config(probe, NULL, SR_CONF_VGAIN); + if (gvar != NULL) { + vgain = g_variant_get_uint64(gvar); + g_variant_unref(gvar); + } + gvar = _dev_inst->get_config(probe, NULL, SR_CONF_VGAIN_DEFAULT); + if (gvar != NULL) { + vgain_default = g_variant_get_uint64(gvar); + g_variant_unref(gvar); + } + gvar = _dev_inst->get_config(probe, NULL, SR_CONF_VGAIN_RANGE); + if (gvar != NULL) { + vgain_range = g_variant_get_uint16(gvar); + g_variant_unref(gvar); + } + + QSlider *gain_slider = new QSlider(Qt::Horizontal, this); + gain_slider->setRange(-vgain_range/2, vgain_range/2); + gain_slider->setValue(vgain - vgain_default); + gain_slider->setObjectName(VGAIN+probe->index); + QString gain_string = "Channel" + QString::number(probe->index) + VGAIN; + QLabel *gain_label = new QLabel(gain_string, this); + _flayout->addRow(gain_label, gain_slider); + _slider_list.push_back(gain_slider); + _label_list.push_back(gain_label); + + uint64_t voff, voff_default; + uint16_t voff_range; + gvar = _dev_inst->get_config(probe, NULL, SR_CONF_VOFF); + if (gvar != NULL) { + voff = g_variant_get_uint16(gvar); + g_variant_unref(gvar); + } + gvar = _dev_inst->get_config(probe, NULL, SR_CONF_VOFF_DEFAULT); + if (gvar != NULL) { + voff_default = g_variant_get_uint16(gvar); + g_variant_unref(gvar); + } + gvar = _dev_inst->get_config(probe, NULL, SR_CONF_VOFF_RANGE); + if (gvar != NULL) { + voff_range = g_variant_get_uint16(gvar); + g_variant_unref(gvar); + } + QSlider *off_slider = new QSlider(Qt::Horizontal, this); + off_slider->setRange(0, voff_range); + off_slider->setValue(voff); + off_slider->setObjectName(VOFF+probe->index); + QString off_string = "Channel" + QString::number(probe->index) + VOFF; + QLabel *off_label = new QLabel(off_string, this); + _flayout->addRow(off_label, off_slider); + _slider_list.push_back(off_slider); + _label_list.push_back(off_label); + + connect(gain_slider, SIGNAL(valueChanged(int)), this, SLOT(set_value(int))); + connect(off_slider, SIGNAL(valueChanged(int)), this, SLOT(set_value(int))); + } + + update(); +} + +void Calibration::accept() +{ + using namespace Qt; + _dev_inst->set_config(NULL, NULL, SR_CONF_CALI, g_variant_new_boolean(false)); + QDialog::accept(); +} + +void Calibration::reject() +{ + using namespace Qt; + _dev_inst->set_config(NULL, NULL, SR_CONF_CALI, g_variant_new_boolean(false)); + QDialog::reject(); +} + +void Calibration::set_value(int value) +{ + QSlider* sc = dynamic_cast(sender()); + + for (const GSList *l = _dev_inst->dev_inst()->channels; l; l = l->next) { + sr_channel *const probe = (sr_channel*)l->data; + assert(probe); + if (sc->objectName() == VGAIN+probe->index) { + uint64_t vgain_default; + GVariant* gvar = _dev_inst->get_config(probe, NULL, SR_CONF_VGAIN_DEFAULT); + if (gvar != NULL) { + vgain_default = g_variant_get_uint64(gvar); + g_variant_unref(gvar); + _dev_inst->set_config(probe, NULL, SR_CONF_VGAIN, + g_variant_new_uint64(value+vgain_default)); + } + break; + } else if (sc->objectName() == VOFF+probe->index) { + _dev_inst->set_config(probe, NULL, SR_CONF_VOFF, + g_variant_new_uint16(value)); + break; + } + } +} + +void Calibration::on_save() +{ + this->hide(); + QFuture future; + future = QtConcurrent::run([&]{ + QTime dieTime = QTime::currentTime().addSecs(1); + _dev_inst->set_config(NULL, NULL, SR_CONF_ZERO_SET, + g_variant_new_boolean(true)); + while( QTime::currentTime() < dieTime ); + }); + Qt::WindowFlags flags = Qt::CustomizeWindowHint; + QProgressDialog dlg(tr("Save Calibration Result... It can take a while."), + tr("Cancel"),0,0,this,flags); + dlg.setWindowModality(Qt::WindowModal); + dlg.setWindowFlags(Qt::Dialog | Qt::FramelessWindowHint); + dlg.setCancelButton(NULL); + + QFutureWatcher watcher; + connect(&watcher,SIGNAL(finished()),&dlg,SLOT(cancel())); + watcher.setFuture(future); + + dlg.exec(); + this->show(); +} + +void Calibration::on_reset() +{ + this->hide(); + QFuture future; + future = QtConcurrent::run([&]{ + QTime dieTime = QTime::currentTime().addSecs(1); + _dev_inst->set_config(NULL, NULL, SR_CONF_ZERO_LOAD, + g_variant_new_boolean(true)); + reload_value(); + while( QTime::currentTime() < dieTime ); + }); + Qt::WindowFlags flags = Qt::CustomizeWindowHint; + QProgressDialog dlg(tr("Reset Calibration Result... It can take a while."), + tr("Cancel"),0,0,this,flags); + dlg.setWindowModality(Qt::WindowModal); + dlg.setWindowFlags(Qt::Dialog | Qt::FramelessWindowHint); + dlg.setCancelButton(NULL); + + QFutureWatcher watcher; + connect(&watcher,SIGNAL(finished()),&dlg,SLOT(cancel())); + watcher.setFuture(future); + + dlg.exec(); + this->show(); +} + +void Calibration::reload_value() +{ + for (const GSList *l = _dev_inst->dev_inst()->channels; l; l = l->next) { + sr_channel *const probe = (sr_channel*)l->data; + assert(probe); + + uint64_t vgain, vgain_default; + uint16_t vgain_range; + GVariant* gvar = _dev_inst->get_config(probe, NULL, SR_CONF_VGAIN); + if (gvar != NULL) { + vgain = g_variant_get_uint64(gvar); + g_variant_unref(gvar); + } + gvar = _dev_inst->get_config(probe, NULL, SR_CONF_VGAIN_DEFAULT); + if (gvar != NULL) { + vgain_default = g_variant_get_uint64(gvar); + g_variant_unref(gvar); + } + gvar = _dev_inst->get_config(probe, NULL, SR_CONF_VGAIN_RANGE); + if (gvar != NULL) { + vgain_range = g_variant_get_uint16(gvar); + g_variant_unref(gvar); + } + + uint64_t voff; + uint16_t voff_range; + gvar = _dev_inst->get_config(probe, NULL, SR_CONF_VOFF); + if (gvar != NULL) { + voff = g_variant_get_uint16(gvar); + g_variant_unref(gvar); + } + gvar = _dev_inst->get_config(probe, NULL, SR_CONF_VOFF_RANGE); + if (gvar != NULL) { + voff_range = g_variant_get_uint16(gvar); + g_variant_unref(gvar); + } + + for(std::list::iterator i = _slider_list.begin(); + i != _slider_list.end(); i++) { + if ((*i)->objectName() == VGAIN+probe->index) { + (*i)->setRange(-vgain_range/2, vgain_range/2); + (*i)->setValue(vgain - vgain_default); + } else if ((*i)->objectName() == VOFF+probe->index) { + (*i)->setRange(0, voff_range); + (*i)->setValue(voff); + } + } + } +} + +} // namespace dialogs +} // namespace pv diff --git a/DSView/pv/dialogs/calibration.h b/DSView/pv/dialogs/calibration.h new file mode 100755 index 00000000..3cc49827 --- /dev/null +++ b/DSView/pv/dialogs/calibration.h @@ -0,0 +1,77 @@ +/* + * This file is part of the DSView project. + * DSView is based on PulseView. + * + * Copyright (C) 2016 DreamSourceLab + * + * 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 + */ + + +#ifndef DSVIEW_PV_CALIBRATION_H +#define DSVIEW_PV_CALIBRATION_H + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +namespace pv { +namespace dialogs { + +class Calibration : public QDialog +{ + Q_OBJECT + +private: + static const QString VGAIN; + static const QString VOFF; + +public: + Calibration(QWidget *parent); + + void set_device(boost::shared_ptr dev_inst); +protected: + void accept(); + void reject(); + +private slots: + void set_value(int value); + void on_save(); + void on_reset(); + void reload_value(); + +private: + boost::shared_ptr _dev_inst; + + QPushButton *_save_btn; + QPushButton *_reset_btn; + QPushButton *_exit_btn; + QFormLayout *_flayout; + std::list _slider_list; + std::list _label_list; +}; + +} // namespace dialogs +} // namespace pv + +#endif // DSVIEW_PV_CALIBRATION_H diff --git a/DSView/pv/dialogs/deviceoptions.cpp b/DSView/pv/dialogs/deviceoptions.cpp index f6a7874e..b2c8db05 100644 --- a/DSView/pv/dialogs/deviceoptions.cpp +++ b/DSView/pv/dialogs/deviceoptions.cpp @@ -3,7 +3,7 @@ * DSView is based on PulseView. * * Copyright (C) 2012 Joel Holdsworth - * Copyright (C) 2013 DreamSourceLab + * Copyright (C) 2013 DreamSourceLab * * 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 @@ -62,6 +62,10 @@ DeviceOptions::DeviceOptions(QWidget *parent, boost::shared_ptrset_config(NULL, NULL, SR_CONF_CALI, g_variant_new_boolean(true)); +} + void DeviceOptions::mode_check() { bool test; diff --git a/DSView/pv/dialogs/deviceoptions.h b/DSView/pv/dialogs/deviceoptions.h index e8cc4ce7..e1c3c6a3 100644 --- a/DSView/pv/dialogs/deviceoptions.h +++ b/DSView/pv/dialogs/deviceoptions.h @@ -3,7 +3,7 @@ * DSView is based on PulseView. * * Copyright (C) 2012 Joel Holdsworth - * Copyright (C) 2013 DreamSourceLab + * Copyright (C) 2013 DreamSourceLab * * 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 @@ -71,6 +71,7 @@ private slots: void zero_adj(); void mode_check(); void channel_check(); + void on_calibration(); private: boost::shared_ptr _dev_inst; @@ -85,6 +86,7 @@ private: QVBoxLayout _props_box_layout; QPushButton *_config_button; + QPushButton *_cali_button; QDialogButtonBox _button_box; QTimer _mode_check; diff --git a/DSView/pv/dialogs/dsomeasure.cpp b/DSView/pv/dialogs/dsomeasure.cpp index 9bb36909..6f5921fd 100644 --- a/DSView/pv/dialogs/dsomeasure.cpp +++ b/DSView/pv/dialogs/dsomeasure.cpp @@ -2,8 +2,7 @@ * This file is part of the DSView project. * DSView is based on PulseView. * - * Copyright (C) 2012 Joel Holdsworth - * Copyright (C) 2013 DreamSourceLab + * Copyright (C) 2015 DreamSourceLab * * 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 diff --git a/DSView/pv/dialogs/dsomeasure.h b/DSView/pv/dialogs/dsomeasure.h index 05a1ba1a..178b0732 100644 --- a/DSView/pv/dialogs/dsomeasure.h +++ b/DSView/pv/dialogs/dsomeasure.h @@ -2,8 +2,7 @@ * This file is part of the DSView project. * DSView is based on PulseView. * - * Copyright (C) 2012 Joel Holdsworth - * Copyright (C) 2013 DreamSourceLab + * Copyright (C) 2015 DreamSourceLab * * 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 diff --git a/DSView/pv/dialogs/fftoptions.cpp b/DSView/pv/dialogs/fftoptions.cpp index b73ac46e..ed7280b8 100644 --- a/DSView/pv/dialogs/fftoptions.cpp +++ b/DSView/pv/dialogs/fftoptions.cpp @@ -2,8 +2,7 @@ * This file is part of the DSView project. * DSView is based on PulseView. * - * Copyright (C) 2012 Joel Holdsworth - * Copyright (C) 2013 DreamSourceLab + * Copyright (C) 2016 DreamSourceLab * * 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 diff --git a/DSView/pv/dialogs/fftoptions.h b/DSView/pv/dialogs/fftoptions.h index 534ef3e0..2ef9a8e9 100644 --- a/DSView/pv/dialogs/fftoptions.h +++ b/DSView/pv/dialogs/fftoptions.h @@ -2,8 +2,7 @@ * This file is part of the DSView project. * DSView is based on PulseView. * - * Copyright (C) 2012 Joel Holdsworth - * Copyright (C) 2013 DreamSourceLab + * Copyright (C) 2016 DreamSourceLab * * 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 diff --git a/DSView/pv/dialogs/protocolexp.cpp b/DSView/pv/dialogs/protocolexp.cpp index c3baf6bb..ab79796b 100644 --- a/DSView/pv/dialogs/protocolexp.cpp +++ b/DSView/pv/dialogs/protocolexp.cpp @@ -2,8 +2,7 @@ * This file is part of the DSView project. * DSView is based on PulseView. * - * Copyright (C) 2012 Joel Holdsworth - * Copyright (C) 2013 DreamSourceLab + * Copyright (C) 2016 DreamSourceLab * * 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 diff --git a/DSView/pv/dialogs/protocolexp.h b/DSView/pv/dialogs/protocolexp.h index c793a75c..0323082d 100644 --- a/DSView/pv/dialogs/protocolexp.h +++ b/DSView/pv/dialogs/protocolexp.h @@ -2,8 +2,7 @@ * This file is part of the DSView project. * DSView is based on PulseView. * - * Copyright (C) 2012 Joel Holdsworth - * Copyright (C) 2013 DreamSourceLab + * Copyright (C) 2016 DreamSourceLab * * 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 diff --git a/DSView/pv/dialogs/protocollist.cpp b/DSView/pv/dialogs/protocollist.cpp index 6f6e090c..eb605476 100644 --- a/DSView/pv/dialogs/protocollist.cpp +++ b/DSView/pv/dialogs/protocollist.cpp @@ -2,8 +2,7 @@ * This file is part of the DSView project. * DSView is based on PulseView. * - * Copyright (C) 2012 Joel Holdsworth - * Copyright (C) 2013 DreamSourceLab + * Copyright (C) 2016 DreamSourceLab * * 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 diff --git a/DSView/pv/dialogs/protocollist.h b/DSView/pv/dialogs/protocollist.h index 6d315322..0f116f7a 100644 --- a/DSView/pv/dialogs/protocollist.h +++ b/DSView/pv/dialogs/protocollist.h @@ -2,8 +2,7 @@ * This file is part of the DSView project. * DSView is based on PulseView. * - * Copyright (C) 2012 Joel Holdsworth - * Copyright (C) 2013 DreamSourceLab + * Copyright (C) 2016 DreamSourceLab * * 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 diff --git a/DSView/pv/dialogs/search.cpp b/DSView/pv/dialogs/search.cpp index aeaeb464..3f177f60 100644 --- a/DSView/pv/dialogs/search.cpp +++ b/DSView/pv/dialogs/search.cpp @@ -2,8 +2,7 @@ * This file is part of the DSView project. * DSView is based on PulseView. * - * Copyright (C) 2012 Joel Holdsworth - * Copyright (C) 2013 DreamSourceLab + * Copyright (C) 2013 DreamSourceLab * * 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 diff --git a/DSView/pv/dialogs/search.h b/DSView/pv/dialogs/search.h index 79c7533a..9efd762d 100644 --- a/DSView/pv/dialogs/search.h +++ b/DSView/pv/dialogs/search.h @@ -2,8 +2,7 @@ * This file is part of the DSView project. * DSView is based on PulseView. * - * Copyright (C) 2012 Joel Holdsworth - * Copyright (C) 2013 DreamSourceLab + * Copyright (C) 2013 DreamSourceLab * * 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 diff --git a/DSView/pv/dialogs/streamoptions.cpp b/DSView/pv/dialogs/streamoptions.cpp index 610c5beb..7c0432dd 100644 --- a/DSView/pv/dialogs/streamoptions.cpp +++ b/DSView/pv/dialogs/streamoptions.cpp @@ -2,8 +2,7 @@ * This file is part of the DSView project. * DSView is based on PulseView. * - * Copyright (C) 2012 Joel Holdsworth - * Copyright (C) 2013 DreamSourceLab + * Copyright (C) 2013 DreamSourceLab * * 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 diff --git a/DSView/pv/dialogs/streamoptions.h b/DSView/pv/dialogs/streamoptions.h index 03fdcc5b..1c9c148d 100644 --- a/DSView/pv/dialogs/streamoptions.h +++ b/DSView/pv/dialogs/streamoptions.h @@ -2,8 +2,7 @@ * This file is part of the DSView project. * DSView is based on PulseView. * - * Copyright (C) 2012 Joel Holdsworth - * Copyright (C) 2013 DreamSourceLab + * Copyright (C) 2013 DreamSourceLab * * 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 diff --git a/DSView/pv/dialogs/waitingdialog.cpp b/DSView/pv/dialogs/waitingdialog.cpp index 8e2b0e52..cb0c742d 100644 --- a/DSView/pv/dialogs/waitingdialog.cpp +++ b/DSView/pv/dialogs/waitingdialog.cpp @@ -2,8 +2,7 @@ * This file is part of the DSView project. * DSView is based on PulseView. * - * Copyright (C) 2012 Joel Holdsworth - * Copyright (C) 2013 DreamSourceLab + * Copyright (C) 2013 DreamSourceLab * * 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 @@ -27,6 +26,9 @@ #include #include +#include +#include +#include #include "libsigrok4DSL/libsigrok.h" #include "../view/trace.h" @@ -37,7 +39,8 @@ using namespace std; namespace pv { namespace dialogs { -const QString WaitingDialog::TIPS_INFO = tr("Waiting"); +const QString WaitingDialog::TIPS_WAIT = tr("Waiting"); +const QString WaitingDialog::TIPS_FINISHED = tr("Finished!"); WaitingDialog::WaitingDialog(QWidget *parent, boost::shared_ptr dev_inst) : QDialog(parent), @@ -58,7 +61,7 @@ WaitingDialog::WaitingDialog(QWidget *parent, boost::shared_ptrsetMovie(movie); tips = new QLabel(this); - tips->setText(TIPS_INFO); + tips->setText(TIPS_WAIT); QFont font; font.setPointSize(10); font.setBold(true); @@ -79,12 +82,29 @@ WaitingDialog::WaitingDialog(QWidget *parent, boost::shared_ptrstop(); timer->stop(); + QDialog::accept(); - _dev_inst->set_config(NULL, NULL, SR_CONF_ZERO_SET, g_variant_new_boolean(true)); - QDialog::accept(); + QFuture future; + future = QtConcurrent::run([&]{ + QTime dieTime = QTime::currentTime().addSecs(1); + _dev_inst->set_config(NULL, NULL, SR_CONF_ZERO_SET, + g_variant_new_boolean(true)); + while( QTime::currentTime() < dieTime ); + }); + Qt::WindowFlags flags = Qt::CustomizeWindowHint; + QProgressDialog dlg(tr("Save Auto Zero Result... It can take a while."), + tr("Cancel"),0,0,this,flags); + dlg.setWindowModality(Qt::WindowModal); + dlg.setWindowFlags(Qt::Dialog | Qt::FramelessWindowHint); + dlg.setCancelButton(NULL); + + QFutureWatcher watcher; + watcher.setFuture(future); + connect(&watcher,SIGNAL(finished()),&dlg,SLOT(cancel())); + + dlg.exec(); } void WaitingDialog::reject() @@ -94,6 +114,27 @@ void WaitingDialog::reject() movie->stop(); timer->stop(); QDialog::reject(); + + QFuture future; + future = QtConcurrent::run([&]{ + QTime dieTime = QTime::currentTime().addSecs(1); + _dev_inst->set_config(NULL, NULL, SR_CONF_ZERO, g_variant_new_boolean(false)); + _dev_inst->set_config(NULL, NULL, SR_CONF_ZERO_LOAD, + g_variant_new_boolean(true)); + while( QTime::currentTime() < dieTime ); + }); + Qt::WindowFlags flags = Qt::CustomizeWindowHint; + QProgressDialog dlg(tr("Load Current Setting... It can take a while."), + tr("Cancel"),0,0,this,flags); + dlg.setWindowModality(Qt::WindowModal); + dlg.setWindowFlags(Qt::Dialog | Qt::FramelessWindowHint); + dlg.setCancelButton(NULL); + + QFutureWatcher watcher; + connect(&watcher,SIGNAL(finished()),&dlg,SLOT(cancel())); + watcher.setFuture(future); + + dlg.exec(); } void WaitingDialog::start() @@ -105,19 +146,24 @@ void WaitingDialog::start() void WaitingDialog::changeText() { - sr_status status; index++; if(index == WPOINTS_NUM + 1) { - tips->setText(TIPS_INFO); + tips->setText(TIPS_WAIT); index = 0; - sr_status_get(_dev_inst->dev_inst(), &status, 0, 0); - if (!status.zeroing) { - movie->stop(); - movie->jumpToFrame(0); - timer->stop(); - tips->setText(""); - _button_box.buttons().front()->setVisible(true); + + GVariant* gvar = _dev_inst->get_config(NULL, NULL, SR_CONF_ZERO); + if (gvar != NULL) { + bool zero = g_variant_get_boolean(gvar); + g_variant_unref(gvar); + if (!zero) { + movie->stop(); + movie->jumpToFrame(0); + timer->stop(); + tips->setAlignment(Qt::AlignHCenter); + tips->setText(TIPS_FINISHED); + _button_box.buttons().front()->setVisible(true); + } } } else { tips->setText(tips->text()+"."); diff --git a/DSView/pv/dialogs/waitingdialog.h b/DSView/pv/dialogs/waitingdialog.h index a214b45d..4b3480f6 100644 --- a/DSView/pv/dialogs/waitingdialog.h +++ b/DSView/pv/dialogs/waitingdialog.h @@ -2,8 +2,7 @@ * This file is part of the DSView project. * DSView is based on PulseView. * - * Copyright (C) 2012 Joel Holdsworth - * Copyright (C) 2013 DreamSourceLab + * Copyright (C) 2013 DreamSourceLab * * 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 @@ -46,7 +45,8 @@ private: static const int TIP_WIDTH = 100; static const int TIP_HEIGHT = 40; static const int WPOINTS_NUM = 6; - static const QString TIPS_INFO; + static const QString TIPS_WAIT; + static const QString TIPS_FINISHED; public: WaitingDialog(QWidget *parent, boost::shared_ptr dev_inst); diff --git a/DSView/pv/dock/dsotriggerdock.cpp b/DSView/pv/dock/dsotriggerdock.cpp index a6d99456..346b7ea4 100644 --- a/DSView/pv/dock/dsotriggerdock.cpp +++ b/DSView/pv/dock/dsotriggerdock.cpp @@ -2,8 +2,7 @@ * This file is part of the DSView project. * DSView is based on PulseView. * - * Copyright (C) 2012 Joel Holdsworth - * Copyright (C) 2013 DreamSourceLab + * Copyright (C) 2013 DreamSourceLab * * 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 diff --git a/DSView/pv/dock/dsotriggerdock.h b/DSView/pv/dock/dsotriggerdock.h index f5c4ac17..a35521ed 100644 --- a/DSView/pv/dock/dsotriggerdock.h +++ b/DSView/pv/dock/dsotriggerdock.h @@ -2,8 +2,7 @@ * This file is part of the DSView project. * DSView is based on PulseView. * - * Copyright (C) 2012 Joel Holdsworth - * Copyright (C) 2013 DreamSourceLab + * Copyright (C) 2013 DreamSourceLab * * 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 diff --git a/DSView/pv/dock/measuredock.cpp b/DSView/pv/dock/measuredock.cpp index 67a7e2fe..1bac919a 100644 --- a/DSView/pv/dock/measuredock.cpp +++ b/DSView/pv/dock/measuredock.cpp @@ -2,8 +2,7 @@ * This file is part of the DSView project. * DSView is based on PulseView. * - * Copyright (C) 2012 Joel Holdsworth - * Copyright (C) 2013 DreamSourceLab + * Copyright (C) 2013 DreamSourceLab * * 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 diff --git a/DSView/pv/dock/measuredock.h b/DSView/pv/dock/measuredock.h index 8cf455fa..bb941b0c 100644 --- a/DSView/pv/dock/measuredock.h +++ b/DSView/pv/dock/measuredock.h @@ -2,8 +2,7 @@ * This file is part of the DSView project. * DSView is based on PulseView. * - * Copyright (C) 2012 Joel Holdsworth - * Copyright (C) 2013 DreamSourceLab + * Copyright (C) 2013 DreamSourceLab * * 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 diff --git a/DSView/pv/dock/protocoldock.cpp b/DSView/pv/dock/protocoldock.cpp index e57ea47a..b93a13a6 100644 --- a/DSView/pv/dock/protocoldock.cpp +++ b/DSView/pv/dock/protocoldock.cpp @@ -2,8 +2,7 @@ * This file is part of the DSView project. * DSView is based on PulseView. * - * Copyright (C) 2012 Joel Holdsworth - * Copyright (C) 2013 DreamSourceLab + * Copyright (C) 2013 DreamSourceLab * * 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 diff --git a/DSView/pv/dock/protocoldock.h b/DSView/pv/dock/protocoldock.h index dcd1dfd8..71f68db6 100644 --- a/DSView/pv/dock/protocoldock.h +++ b/DSView/pv/dock/protocoldock.h @@ -2,8 +2,7 @@ * This file is part of the DSView project. * DSView is based on PulseView. * - * Copyright (C) 2012 Joel Holdsworth - * Copyright (C) 2013 DreamSourceLab + * Copyright (C) 2013 DreamSourceLab * * 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 diff --git a/DSView/pv/dock/searchdock.cpp b/DSView/pv/dock/searchdock.cpp index 90b1bbab..c3801a31 100644 --- a/DSView/pv/dock/searchdock.cpp +++ b/DSView/pv/dock/searchdock.cpp @@ -2,8 +2,7 @@ * This file is part of the DSView project. * DSView is based on PulseView. * - * Copyright (C) 2012 Joel Holdsworth - * Copyright (C) 2013 DreamSourceLab + * Copyright (C) 2013 DreamSourceLab * * 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 diff --git a/DSView/pv/dock/searchdock.h b/DSView/pv/dock/searchdock.h index 2477dcb4..2583cf1a 100644 --- a/DSView/pv/dock/searchdock.h +++ b/DSView/pv/dock/searchdock.h @@ -2,8 +2,7 @@ * This file is part of the DSView project. * DSView is based on PulseView. * - * Copyright (C) 2012 Joel Holdsworth - * Copyright (C) 2013 DreamSourceLab + * Copyright (C) 2013 DreamSourceLab * * 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 diff --git a/DSView/pv/dock/triggerdock.cpp b/DSView/pv/dock/triggerdock.cpp index a16eb34e..26fbdfa9 100644 --- a/DSView/pv/dock/triggerdock.cpp +++ b/DSView/pv/dock/triggerdock.cpp @@ -2,8 +2,7 @@ * This file is part of the DSView project. * DSView is based on PulseView. * - * Copyright (C) 2012 Joel Holdsworth - * Copyright (C) 2013 DreamSourceLab + * Copyright (C) 2013 DreamSourceLab * * 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 diff --git a/DSView/pv/dock/triggerdock.h b/DSView/pv/dock/triggerdock.h index ece32316..6533b03e 100644 --- a/DSView/pv/dock/triggerdock.h +++ b/DSView/pv/dock/triggerdock.h @@ -2,8 +2,7 @@ * This file is part of the DSView project. * DSView is based on PulseView. * - * Copyright (C) 2012 Joel Holdsworth - * Copyright (C) 2013 DreamSourceLab + * Copyright (C) 2013 DreamSourceLab * * 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 diff --git a/DSView/pv/mainwindow.cpp b/DSView/pv/mainwindow.cpp index 42e81399..fccd54af 100644 --- a/DSView/pv/mainwindow.cpp +++ b/DSView/pv/mainwindow.cpp @@ -3,7 +3,7 @@ * DSView is based on PulseView. * * Copyright (C) 2012 Joel Holdsworth - * Copyright (C) 2013 DreamSourceLab + * Copyright (C) 2013 DreamSourceLab * * 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 @@ -194,6 +194,10 @@ void MainWindow::setup_ui() SLOT(instant_stop())); connect(_sampling_bar, SIGNAL(sample_count_changed()), _trigger_widget, SLOT(device_change())); + connect(_sampling_bar, SIGNAL(show_calibration()), _view, + SLOT(show_calibration())); + connect(_sampling_bar, SIGNAL(hide_calibration()), _view, + SLOT(hide_calibration())); connect(_dso_trigger_widget, SIGNAL(set_trig_pos(quint64)), _view, SLOT(set_trig_pos(quint64))); connect(_protocol_widget, SIGNAL(protocol_updated()), _view, SLOT(signals_changed())); @@ -421,11 +425,7 @@ void MainWindow::run_stop() void MainWindow::instant_stop() { -#ifdef TEST_MODE - disconnect(&test_timer, SIGNAL(timeout()), - this, SLOT(run_stop())); - test_timer_linked = false; -#else + switch(_session.get_capture_state()) { case SigSession::Init: case SigSession::Stopped: @@ -441,16 +441,12 @@ void MainWindow::instant_stop() _session.stop_capture(); break; } -#endif + } void MainWindow::test_data_error() { -#ifdef TEST_MODE - disconnect(&test_timer, SIGNAL(timeout()), - this, SLOT(run_stop())); - test_timer_linked = false; -#endif + _session.stop_capture(); QMessageBox msg(this); msg.setText(tr("Data Error")); @@ -511,11 +507,7 @@ void MainWindow::capture_state_changed(int state) } } } -#ifdef TEST_MODE - if (state == SigSession::Stopped) { - test_timer.start(100); - } -#endif + } } diff --git a/DSView/pv/mainwindow.h b/DSView/pv/mainwindow.h index 36abdf6f..1b96a375 100644 --- a/DSView/pv/mainwindow.h +++ b/DSView/pv/mainwindow.h @@ -3,7 +3,7 @@ * DSView is based on PulseView. * * Copyright (C) 2012 Joel Holdsworth - * Copyright (C) 2013 DreamSourceLab + * Copyright (C) 2013 DreamSourceLab * * 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 @@ -59,6 +59,10 @@ class MeasureDock; class SearchDock; } +namespace dialogs{ +class Calibration; +} + namespace view { class View; } diff --git a/DSView/pv/prop/binding/binding.cpp b/DSView/pv/prop/binding/binding.cpp index 95c9ca3b..2470ba21 100644 --- a/DSView/pv/prop/binding/binding.cpp +++ b/DSView/pv/prop/binding/binding.cpp @@ -3,7 +3,7 @@ * DSView is based on PulseView. * * Copyright (C) 2012 Joel Holdsworth - * Copyright (C) 2013 DreamSourceLab + * Copyright (C) 2013 DreamSourceLab * * 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 diff --git a/DSView/pv/prop/binding/binding.h b/DSView/pv/prop/binding/binding.h index ab7758b5..ca1462c2 100644 --- a/DSView/pv/prop/binding/binding.h +++ b/DSView/pv/prop/binding/binding.h @@ -3,7 +3,7 @@ * DSView is based on PulseView. * * Copyright (C) 2012 Joel Holdsworth - * Copyright (C) 2013 DreamSourceLab + * Copyright (C) 2013 DreamSourceLab * * 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 diff --git a/DSView/pv/prop/binding/binding_deviceoptions.cpp b/DSView/pv/prop/binding/binding_deviceoptions.cpp index c8c1e9a0..ea9a3c99 100644 --- a/DSView/pv/prop/binding/binding_deviceoptions.cpp +++ b/DSView/pv/prop/binding/binding_deviceoptions.cpp @@ -3,7 +3,7 @@ * DSView is based on PulseView. * * Copyright (C) 2012 Joel Holdsworth - * Copyright (C) 2013 DreamSourceLab + * Copyright (C) 2013 DreamSourceLab * * 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 diff --git a/DSView/pv/prop/binding/deviceoptions.h b/DSView/pv/prop/binding/deviceoptions.h index a4b99744..f9f8d42b 100644 --- a/DSView/pv/prop/binding/deviceoptions.h +++ b/DSView/pv/prop/binding/deviceoptions.h @@ -3,7 +3,7 @@ * DSView is based on PulseView. * * Copyright (C) 2012 Joel Holdsworth - * Copyright (C) 2013 DreamSourceLab + * Copyright (C) 2013 DreamSourceLab * * 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 diff --git a/DSView/pv/prop/bool.cpp b/DSView/pv/prop/bool.cpp index 9d7e4961..2dd84890 100644 --- a/DSView/pv/prop/bool.cpp +++ b/DSView/pv/prop/bool.cpp @@ -3,7 +3,6 @@ * DSView is based on PulseView. * * Copyright (C) 2012 Joel Holdsworth - * Copyright (C) 2013 DreamSourceLab * * 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 diff --git a/DSView/pv/prop/bool.h b/DSView/pv/prop/bool.h index 3976606d..a1b3e2f6 100644 --- a/DSView/pv/prop/bool.h +++ b/DSView/pv/prop/bool.h @@ -3,7 +3,6 @@ * DSView is based on PulseView. * * Copyright (C) 2012 Joel Holdsworth - * Copyright (C) 2013 DreamSourceLab * * 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 diff --git a/DSView/pv/prop/double.cpp b/DSView/pv/prop/double.cpp index 2bd4a9ff..73a174bb 100644 --- a/DSView/pv/prop/double.cpp +++ b/DSView/pv/prop/double.cpp @@ -3,7 +3,6 @@ * DSView is based on PulseView. * * Copyright (C) 2012 Joel Holdsworth - * Copyright (C) 2013 DreamSourceLab * * 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 diff --git a/DSView/pv/prop/double.h b/DSView/pv/prop/double.h index e26d5d04..7eb4493d 100644 --- a/DSView/pv/prop/double.h +++ b/DSView/pv/prop/double.h @@ -3,7 +3,6 @@ * DSView is based on PulseView. * * Copyright (C) 2012 Joel Holdsworth - * Copyright (C) 2013 DreamSourceLab * * 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 diff --git a/DSView/pv/prop/enum.cpp b/DSView/pv/prop/enum.cpp index 8c51f554..2aee897f 100644 --- a/DSView/pv/prop/enum.cpp +++ b/DSView/pv/prop/enum.cpp @@ -3,7 +3,6 @@ * DSView is based on PulseView. * * Copyright (C) 2012 Joel Holdsworth - * Copyright (C) 2013 DreamSourceLab * * 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 diff --git a/DSView/pv/prop/enum.h b/DSView/pv/prop/enum.h index dbeb1cf2..a223565a 100644 --- a/DSView/pv/prop/enum.h +++ b/DSView/pv/prop/enum.h @@ -3,7 +3,6 @@ * DSView is based on PulseView. * * Copyright (C) 2012 Joel Holdsworth - * Copyright (C) 2013 DreamSourceLab * * 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 diff --git a/DSView/pv/prop/int.cpp b/DSView/pv/prop/int.cpp index 1c9e79cd..b3ea0c69 100644 --- a/DSView/pv/prop/int.cpp +++ b/DSView/pv/prop/int.cpp @@ -3,7 +3,6 @@ * DSView is based on PulseView. * * Copyright (C) 2012 Joel Holdsworth - * Copyright (C) 2013 DreamSourceLab * * 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 diff --git a/DSView/pv/prop/int.h b/DSView/pv/prop/int.h index 776b4f98..daa4c1d3 100644 --- a/DSView/pv/prop/int.h +++ b/DSView/pv/prop/int.h @@ -3,7 +3,6 @@ * DSView is based on PulseView. * * Copyright (C) 2012 Joel Holdsworth - * Copyright (C) 2013 DreamSourceLab * * 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 diff --git a/DSView/pv/prop/property.cpp b/DSView/pv/prop/property.cpp index 48ca4a2c..af793309 100644 --- a/DSView/pv/prop/property.cpp +++ b/DSView/pv/prop/property.cpp @@ -3,7 +3,6 @@ * DSView is based on PulseView. * * Copyright (C) 2012 Joel Holdsworth - * Copyright (C) 2013 DreamSourceLab * * 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 diff --git a/DSView/pv/prop/property.h b/DSView/pv/prop/property.h index 6ef784d5..3a1b165f 100644 --- a/DSView/pv/prop/property.h +++ b/DSView/pv/prop/property.h @@ -3,7 +3,6 @@ * DSView is based on PulseView. * * Copyright (C) 2012 Joel Holdsworth - * Copyright (C) 2013 DreamSourceLab * * 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 diff --git a/DSView/pv/sigsession.cpp b/DSView/pv/sigsession.cpp index 51141f24..6584c09b 100644 --- a/DSView/pv/sigsession.cpp +++ b/DSView/pv/sigsession.cpp @@ -3,7 +3,7 @@ * DSView is based on PulseView. * * Copyright (C) 2012 Joel Holdsworth - * Copyright (C) 2013 DreamSourceLab + * Copyright (C) 2013 DreamSourceLab * * 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 diff --git a/DSView/pv/sigsession.h b/DSView/pv/sigsession.h index 5092b19b..95769fe6 100644 --- a/DSView/pv/sigsession.h +++ b/DSView/pv/sigsession.h @@ -3,7 +3,7 @@ * DSView is based on PulseView. * * Copyright (C) 2012 Joel Holdsworth - * Copyright (C) 2013 DreamSourceLab + * Copyright (C) 2013 DreamSourceLab * * 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 diff --git a/DSView/pv/toolbars/filebar.cpp b/DSView/pv/toolbars/filebar.cpp index 3c5efd30..ecf93a5a 100644 --- a/DSView/pv/toolbars/filebar.cpp +++ b/DSView/pv/toolbars/filebar.cpp @@ -2,8 +2,7 @@ * This file is part of the DSView project. * DSView is based on PulseView. * - * Copyright (C) 2012 Joel Holdsworth - * Copyright (C) 2013 DreamSourceLab + * Copyright (C) 2013 DreamSourceLab * * 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 diff --git a/DSView/pv/toolbars/filebar.h b/DSView/pv/toolbars/filebar.h index 6f4c2c40..a77f3928 100644 --- a/DSView/pv/toolbars/filebar.h +++ b/DSView/pv/toolbars/filebar.h @@ -2,8 +2,7 @@ * This file is part of the DSView project. * DSView is based on PulseView. * - * Copyright (C) 2012 Joel Holdsworth - * Copyright (C) 2013 DreamSourceLab + * Copyright (C) 2013 DreamSourceLab * * 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 diff --git a/DSView/pv/toolbars/logobar.cpp b/DSView/pv/toolbars/logobar.cpp index 00063406..8f19e614 100644 --- a/DSView/pv/toolbars/logobar.cpp +++ b/DSView/pv/toolbars/logobar.cpp @@ -2,8 +2,7 @@ * This file is part of the DSView project. * DSView is based on PulseView. * - * Copyright (C) 2012 Joel Holdsworth - * Copyright (C) 2013 DreamSourceLab + * Copyright (C) 2013 DreamSourceLab * * 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 diff --git a/DSView/pv/toolbars/logobar.h b/DSView/pv/toolbars/logobar.h index 7e8ac9bc..ad486b9e 100644 --- a/DSView/pv/toolbars/logobar.h +++ b/DSView/pv/toolbars/logobar.h @@ -2,8 +2,7 @@ * This file is part of the DSView project. * DSView is based on PulseView. * - * Copyright (C) 2012 Joel Holdsworth - * Copyright (C) 2013 DreamSourceLab + * Copyright (C) 2013 DreamSourceLab * * 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 diff --git a/DSView/pv/toolbars/samplingbar.cpp b/DSView/pv/toolbars/samplingbar.cpp index 05616ca3..fd3c3129 100644 --- a/DSView/pv/toolbars/samplingbar.cpp +++ b/DSView/pv/toolbars/samplingbar.cpp @@ -2,8 +2,7 @@ * This file is part of the DSView project. * DSView is based on PulseView. * - * Copyright (C) 2012 Joel Holdsworth - * Copyright (C) 2013 DreamSourceLab + * Copyright (C) 2013 DreamSourceLab * * 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 @@ -204,6 +203,8 @@ shared_ptr SamplingBar::get_selected_device() const void SamplingBar::on_configure() { + hide_calibration(); + int ret; shared_ptr dev_inst = get_selected_device(); assert(dev_inst); @@ -215,17 +216,29 @@ void SamplingBar::on_configure() update_sample_count_selector(); update_sample_rate_selector(); commit_sample_rate(); + + GVariant* gvar; + if (dev_inst->dev_inst()->mode == DSO) { + gvar = dev_inst->get_config(NULL, NULL, SR_CONF_ZERO); + if (gvar != NULL) { + bool zero = g_variant_get_boolean(gvar); + g_variant_unref(gvar); + if (zero) + zero_adj(); + } + + gvar = dev_inst->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(); + } + } + } } - GVariant* gvar = dev_inst->get_config(NULL, NULL, SR_CONF_ZERO); - if (gvar != NULL) { - bool zero = g_variant_get_boolean(gvar); - g_variant_unref(gvar); - if (zero) - zero_adj(); - } - - gvar = dev_inst->get_config(NULL, NULL, SR_CONF_TEST); + GVariant* gvar = dev_inst->get_config(NULL, NULL, SR_CONF_TEST); if (gvar != NULL) { bool test = g_variant_get_boolean(gvar); g_variant_unref(gvar); @@ -316,17 +329,6 @@ void SamplingBar::set_sampling(bool sampling) _run_stop_button.setEnabled(true); _instant_button.setEnabled(true); } else { -// bool running = false; -// boost::shared_ptr dev_inst = get_selected_device(); -// assert(dev_inst); -// while (!running) { -// GVariant* gvar = dev_inst->get_config(NULL, NULL, SR_CONF_STATUS); -// if (gvar != NULL) { -// running = g_variant_get_boolean(gvar); -// g_variant_unref(gvar); -// } -// g_usleep(10000); -// } g_usleep(100000); if (_instant) _instant_button.setEnabled(true); @@ -667,10 +669,18 @@ void SamplingBar::on_run_stop() QMessageBox msg(this); msg.setText(tr("Zero Adjustment")); msg.setInformativeText(tr("Please adjust zero skew and save the result!")); - msg.setStandardButtons(QMessageBox::Ok); + //msg.setStandardButtons(QMessageBox::Ok); + msg.addButton(tr("Ok"), QMessageBox::AcceptRole); + msg.addButton(tr("Skip"), QMessageBox::RejectRole); msg.setIcon(QMessageBox::Warning); - msg.exec(); - zero_adj(); + int ret = msg.exec(); + if ( ret == QMessageBox::AcceptRole) { + zero_adj(); + } else { + dev_inst->set_config(NULL, NULL, SR_CONF_ZERO, g_variant_new_boolean(false)); + enable_run_stop(true); + enable_instant(true); + } return; } } @@ -696,14 +706,19 @@ void SamplingBar::on_instant_stop() if (zero) { QMessageBox msg(this); msg.setText(tr("Zero Adjustment")); - if(strcmp(dev_inst->dev_inst()->driver->name, "DSLogic") == 0) - msg.setInformativeText(tr("Please adjust zero skew and save the result!\nPlease left both of channels unconnect for zero adjustment!")); - else - msg.setInformativeText(tr("Please adjust zero skew and save the result!")); - msg.setStandardButtons(QMessageBox::Ok); + msg.setInformativeText(tr("Zero adjustment program will be started. Please keep all channels out of singal input. It can take a while!")); + //msg.setStandardButtons(QMessageBox::Ok); + msg.addButton(tr("Ok"), QMessageBox::AcceptRole); + msg.addButton(tr("Skip"), QMessageBox::RejectRole); msg.setIcon(QMessageBox::Warning); - msg.exec(); - zero_adj(); + int ret = msg.exec(); + if ( ret == QMessageBox::AcceptRole) { + zero_adj(); + } else { + dev_inst->set_config(NULL, NULL, SR_CONF_ZERO, g_variant_new_boolean(false)); + enable_run_stop(true); + enable_instant(true); + } return; } } diff --git a/DSView/pv/toolbars/samplingbar.h b/DSView/pv/toolbars/samplingbar.h index 377246e4..7c9fa4bb 100644 --- a/DSView/pv/toolbars/samplingbar.h +++ b/DSView/pv/toolbars/samplingbar.h @@ -2,8 +2,7 @@ * This file is part of the DSView project. * DSView is based on PulseView. * - * Copyright (C) 2012 Joel Holdsworth - * Copyright (C) 2013 DreamSourceLab + * Copyright (C) 2013 DreamSourceLab * * 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 @@ -52,6 +51,7 @@ class DevInst; namespace dialogs { class deviceoptions; +class Calibration; } namespace toolbars { @@ -96,6 +96,8 @@ signals: void device_selected(); void device_updated(); void sample_count_changed(); + void show_calibration(); + void hide_calibration(); private: void update_sample_rate_selector(); diff --git a/DSView/pv/toolbars/trigbar.cpp b/DSView/pv/toolbars/trigbar.cpp index bbd580a2..50207684 100644 --- a/DSView/pv/toolbars/trigbar.cpp +++ b/DSView/pv/toolbars/trigbar.cpp @@ -2,8 +2,7 @@ * This file is part of the DSView project. * DSView is based on PulseView. * - * Copyright (C) 2012 Joel Holdsworth - * Copyright (C) 2013 DreamSourceLab + * Copyright (C) 2013 DreamSourceLab * * 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 diff --git a/DSView/pv/toolbars/trigbar.h b/DSView/pv/toolbars/trigbar.h index 7100799e..53faf1d3 100644 --- a/DSView/pv/toolbars/trigbar.h +++ b/DSView/pv/toolbars/trigbar.h @@ -2,8 +2,7 @@ * This file is part of the DSView project. * DSView is based on PulseView. * - * Copyright (C) 2012 Joel Holdsworth - * Copyright (C) 2013 DreamSourceLab + * Copyright (C) 2013 DreamSourceLab * * 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 diff --git a/DSView/pv/view/analogsignal.cpp b/DSView/pv/view/analogsignal.cpp index d9cc46b2..d9726117 100644 --- a/DSView/pv/view/analogsignal.cpp +++ b/DSView/pv/view/analogsignal.cpp @@ -3,7 +3,7 @@ * DSView is based on PulseView. * * Copyright (C) 2012 Joel Holdsworth - * Copyright (C) 2013 DreamSourceLab + * Copyright (C) 2013 DreamSourceLab * * 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 diff --git a/DSView/pv/view/analogsignal.h b/DSView/pv/view/analogsignal.h index b241e848..b8810fdb 100644 --- a/DSView/pv/view/analogsignal.h +++ b/DSView/pv/view/analogsignal.h @@ -2,8 +2,7 @@ * This file is part of the DSView project. * DSView is based on PulseView. * - * Copyright (C) 2012 Joel Holdsworth - * Copyright (C) 2013 DreamSourceLab + * Copyright (C) 2013 DreamSourceLab * * 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 diff --git a/DSView/pv/view/cursor.cpp b/DSView/pv/view/cursor.cpp index 9f108e9a..aaa24582 100644 --- a/DSView/pv/view/cursor.cpp +++ b/DSView/pv/view/cursor.cpp @@ -3,7 +3,7 @@ * DSView is based on PulseView. * * Copyright (C) 2012 Joel Holdsworth - * Copyright (C) 2013 DreamSourceLab + * Copyright (C) 2013 DreamSourceLab * * 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 diff --git a/DSView/pv/view/cursor.h b/DSView/pv/view/cursor.h index d1b284b7..b1205874 100644 --- a/DSView/pv/view/cursor.h +++ b/DSView/pv/view/cursor.h @@ -3,7 +3,7 @@ * DSView is based on PulseView. * * Copyright (C) 2012 Joel Holdsworth - * Copyright (C) 2013 DreamSourceLab + * Copyright (C) 2013 DreamSourceLab * * 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 diff --git a/DSView/pv/view/decodetrace.cpp b/DSView/pv/view/decodetrace.cpp index e16c0f8b..8d20ddca 100644 --- a/DSView/pv/view/decodetrace.cpp +++ b/DSView/pv/view/decodetrace.cpp @@ -2,6 +2,7 @@ * This file is part of the PulseView project. * * Copyright (C) 2012 Joel Holdsworth + * Copyright (C) 2014 DreamSourceLab * * 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 diff --git a/DSView/pv/view/decodetrace.h b/DSView/pv/view/decodetrace.h index 39db68e6..12269b8c 100644 --- a/DSView/pv/view/decodetrace.h +++ b/DSView/pv/view/decodetrace.h @@ -2,6 +2,7 @@ * This file is part of the PulseView project. * * Copyright (C) 2012 Joel Holdsworth + * Copyright (C) 2014 DreamSourceLab * * 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 diff --git a/DSView/pv/view/devmode.cpp b/DSView/pv/view/devmode.cpp index 2fb0a2b7..b782a11c 100644 --- a/DSView/pv/view/devmode.cpp +++ b/DSView/pv/view/devmode.cpp @@ -2,7 +2,7 @@ * This file is part of the DSView project. * DSView is based on PulseView. * - * Copyright (C) 2014 DreamSourceLab + * Copyright (C) 2014 DreamSourceLab * * 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 diff --git a/DSView/pv/view/devmode.h b/DSView/pv/view/devmode.h index 3e114fbb..033f55ac 100644 --- a/DSView/pv/view/devmode.h +++ b/DSView/pv/view/devmode.h @@ -2,7 +2,7 @@ * This file is part of the DSView project. * DSView is based on PulseView. * - * Copyright (C) 2014 DreamSourceLab + * Copyright (C) 2014 DreamSourceLab * * 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 diff --git a/DSView/pv/view/dsldial.cpp b/DSView/pv/view/dsldial.cpp index 454dc620..3d0b1300 100644 --- a/DSView/pv/view/dsldial.cpp +++ b/DSView/pv/view/dsldial.cpp @@ -1,3 +1,24 @@ +/* + * This file is part of the DSView project. + * DSView is based on PulseView. + * + * Copyright (C) 2014 DreamSourceLab + * + * 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 "dsldial.h" #include diff --git a/DSView/pv/view/dsldial.h b/DSView/pv/view/dsldial.h index ddb7ea61..392cf89e 100644 --- a/DSView/pv/view/dsldial.h +++ b/DSView/pv/view/dsldial.h @@ -1,3 +1,24 @@ +/* + * This file is part of the DSView project. + * DSView is based on PulseView. + * + * Copyright (C) 2013 DreamSourceLab + * + * 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 + */ + #ifndef DSVIEW_PV_VIEW_DSLDIAL_H #define DSVIEW_PV_VIEW_DSLDIAL_H diff --git a/DSView/pv/view/dsosignal.cpp b/DSView/pv/view/dsosignal.cpp index 4113c65f..7ee1befd 100644 --- a/DSView/pv/view/dsosignal.cpp +++ b/DSView/pv/view/dsosignal.cpp @@ -2,7 +2,7 @@ * This file is part of the DSView project. * DSView is based on PulseView. * - * Copyright (C) 2013 DreamSourceLab + * Copyright (C) 2013 DreamSourceLab * * 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 @@ -263,6 +263,7 @@ bool DsoSignal::go_vDialPre() if (_view->session().get_capture_state() == SigSession::Stopped) _scale *= pre_vdiv/_vDial->get_value(); update_zeroPos(); + _view->update_calibration(); _view->set_update(_viewport, true); _view->update(); return true; @@ -282,6 +283,7 @@ bool DsoSignal::go_vDialNext() if (_view->session().get_capture_state() == SigSession::Stopped) _scale *= pre_vdiv/_vDial->get_value(); update_zeroPos(); + _view->update_calibration(); _view->set_update(_viewport, true); _view->update(); return true; @@ -469,7 +471,7 @@ bool DsoSignal::load_settings() vdiv = g_variant_get_uint64(gvar); g_variant_unref(gvar); } else { - qDebug() << "ERROR: config_get SR_CONF_TIMEBASE failed."; + qDebug() << "ERROR: config_get SR_CONF_VDIV failed."; return false; } gvar = _dev_inst->get_config(_probe, NULL, SR_CONF_FACTOR); @@ -477,7 +479,7 @@ bool DsoSignal::load_settings() vfactor = g_variant_get_uint64(gvar); g_variant_unref(gvar); } else { - qDebug() << "ERROR: config_get SR_CONF_TIMEBASE failed."; + qDebug() << "ERROR: config_get SR_CONF_FACTOR failed."; return false; } @@ -506,12 +508,29 @@ bool DsoSignal::load_settings() vpos = g_variant_get_double(gvar); g_variant_unref(gvar); } else { - qDebug() << "ERROR: config_get SR_CONF_COUPLING failed."; + qDebug() << "ERROR: config_get SR_CONF_VPOS failed."; return false; } _zeroPos = min(max((0.5 - vpos / (_vDial->get_value() * DS_CONF_DSO_VDIVS)), 0.0), 1.0); _zero_off = _zeroPos * 255; + // -- trig_value + uint8_t trigger_value; + gvar = _dev_inst->get_config(_probe, NULL, SR_CONF_TRIGGER_VALUE); + if (gvar != NULL) { + trigger_value = g_variant_get_byte(gvar); + g_variant_unref(gvar); + } else { + qDebug() << "ERROR: config_get SR_CONF_TRIGGER_VALUE failed."; + return false; + } + bool isDSCope = (strcmp(_dev_inst->dev_inst()->driver->name, "DSCope") == 0); + if (isDSCope) { + _trig_vpos = min(max(trigger_value/255.0, 0+TrigMargin), 1-TrigMargin); + } else { + _trig_vpos = min(max(_zeroPos + ((trigger_value - 0x80) / 255.0), 0+TrigMargin), 1-TrigMargin); + } + if (_view) { _view->set_update(_viewport, true); _view->update(); @@ -519,6 +538,35 @@ bool DsoSignal::load_settings() return true; } +int DsoSignal::commit_settings() +{ + int ret; + // -- enable + ret = _dev_inst->set_config(_probe, NULL, SR_CONF_EN_CH, + g_variant_new_boolean(enabled())); + + // -- hdiv + ret = _dev_inst->set_config(_probe, NULL, SR_CONF_TIMEBASE, + g_variant_new_uint64(_hDial->get_value())); + + // -- vdiv + ret = _dev_inst->set_config(_probe, NULL, SR_CONF_VDIV, + g_variant_new_uint64(_vDial->get_value())); + ret = _dev_inst->set_config(_probe, NULL, SR_CONF_FACTOR, + g_variant_new_uint64(_vDial->get_factor())); + + // -- coupling + ret = _dev_inst->set_config(_probe, NULL, SR_CONF_COUPLING, + g_variant_new_byte(_acCoupling)); + + // -- vpos + double vpos_off = (0.5 - (get_zeroPos() - UpMargin) * 1.0/get_view_rect().height()) * _vDial->get_value() * DS_CONF_DSO_VDIVS; + ret = _dev_inst->set_config(_probe, NULL, SR_CONF_VPOS, + g_variant_new_double(vpos_off)); + + return ret; +} + uint64_t DsoSignal::get_vDialValue() const { return _vDial->get_value(); @@ -571,14 +619,14 @@ void DsoSignal::set_trig_vpos(int pos) double delta = min((double)max(pos - UpMargin, 0), get_view_rect().height()) * 1.0 / get_view_rect().height(); bool isDSCope = (strcmp(_dev_inst->dev_inst()->driver->name, "DSCope") == 0); if (isDSCope) { - _trig_vpos = min(max(delta, 0+TrigMargin), 1-TrigMargin); - trig_value = delta * 255; + trig_value = delta * 255.0 + 0.5; + _trig_vpos = min(max(trig_value/255.0, 0+TrigMargin), 1-TrigMargin); } else { delta = delta - _zeroPos; delta = min(delta, 0.5); delta = max(delta, -0.5); - _trig_vpos = min(max(_zeroPos + delta, 0+TrigMargin), 1-TrigMargin); trig_value = (delta * 255.0f + 0x80); + _trig_vpos = min(max(_zeroPos + (trig_value - 0x80) / 255.0, 0+TrigMargin), 1-TrigMargin); } _dev_inst->set_config(_probe, NULL, SR_CONF_TRIGGER_VALUE, g_variant_new_byte(trig_value)); @@ -591,14 +639,15 @@ void DsoSignal::set_trigRate(double rate) double delta = rate; bool isDSCope = (strcmp(_dev_inst->dev_inst()->driver->name, "DSCope") == 0); if (isDSCope) { - _trig_vpos = min(max(delta, 0+TrigMargin), 1-TrigMargin); - trig_value = delta * 255; + trig_value = delta * 255.0 + 0.5; + _trig_vpos = min(max(trig_value/255.0, 0+TrigMargin), 1-TrigMargin); + } else { delta = delta - _zeroPos; delta = min(delta, 0.5); delta = max(delta, -0.5); - _trig_vpos = min(max(_zeroPos + delta, 0+TrigMargin), 1-TrigMargin); trig_value = (delta * 255.0f + 0x80); + _trig_vpos = min(max(_zeroPos + (trig_value - 0x80) / 255.0, 0+TrigMargin), 1-TrigMargin); } _dev_inst->set_config(_probe, NULL, SR_CONF_TRIGGER_VALUE, g_variant_new_byte(trig_value)); @@ -764,8 +813,8 @@ void DsoSignal::paint_back(QPainter &p, int left, int right) p.setPen(Trace::dsLightBlue); p.drawLine(left, UpMargin/2, left + width, UpMargin/2); - const uint64_t sample_len = _view->session().get_device()->get_sample_limit(); - const double samplerate = _view->session().get_device()->get_sample_rate(); + const uint64_t sample_len = _dev_inst->get_sample_limit(); + const double samplerate = _dev_inst->get_sample_rate(); const double samples_per_pixel = samplerate * _view->scale(); const double shown_rate = min(samples_per_pixel * width * 1.0 / sample_len, 1.0); const double start_time = _data->get_start_time(); @@ -845,7 +894,7 @@ void DsoSignal::paint_mid(QPainter &p, int left, int right) const double pixels_offset = offset / scale; //const double samplerate = _data->samplerate(); - const double samplerate = _view->session().get_device()->get_sample_rate(); + const double samplerate = _dev_inst->get_sample_rate(); const double start_time = _data->get_start_time(); const int64_t last_sample = max((int64_t)(snapshot->get_sample_count() - 1), (int64_t)0); const double samples_per_pixel = samplerate * scale; @@ -1153,7 +1202,7 @@ bool DsoSignal::mouse_press(int right, const QPoint pt) if (strcmp(_view->session().get_device()->dev_inst()->driver->name, "DSLogic") == 0) set_acCoupling((get_acCoupling()+1)%2); else - set_acCoupling((get_acCoupling()+1)%3); + set_acCoupling((get_acCoupling()+1)%2); } else if (x1_rect.contains(pt)) { set_factor(1); } else if (x10_rect.contains(pt)) { @@ -1282,7 +1331,7 @@ void DsoSignal::paint_measure(QPainter &p) double value_p2p = value_max - value_min; _period = (count == 0) ? period * 10.0 : period * 10.0 / count; const int channel_count = _view->session().get_ch_num(SR_CHANNEL_DSO); - uint64_t sample_rate = _view->session().get_device()->get_sample_rate(); + uint64_t sample_rate = _dev_inst->get_sample_rate(); _period = _period * 200.0 / (channel_count * sample_rate * 1.0 / SR_MHZ(1)); _ms_string[DSO_MS_VMAX] = "Vmax: " + (abs(value_max) > 1000 ? QString::number(value_max/1000.0, 'f', 2) + "V" : QString::number(value_max, 'f', 2) + "mV"); _ms_string[DSO_MS_VMIN] = "Vmin: " + (abs(value_min) > 1000 ? QString::number(value_min/1000.0, 'f', 2) + "V" : QString::number(value_min, 'f', 2) + "mV"); @@ -1457,7 +1506,7 @@ bool DsoSignal::measure(const QPointF &p) assert(scale > 0); const double offset = _view->offset(); const double pixels_offset = offset / scale; - const double samplerate = _view->session().get_device()->get_sample_rate(); + const double samplerate = _dev_inst->get_sample_rate(); const double samples_per_pixel = samplerate * scale; _hover_index = floor((p.x() + pixels_offset) * samples_per_pixel+0.5); diff --git a/DSView/pv/view/dsosignal.h b/DSView/pv/view/dsosignal.h index 032287e6..67c67da7 100644 --- a/DSView/pv/view/dsosignal.h +++ b/DSView/pv/view/dsosignal.h @@ -2,7 +2,7 @@ * This file is part of the DSView project. * DSView is based on PulseView. * - * Copyright (C) 2013 DreamSourceLab + * Copyright (C) 2013 DreamSourceLab * * 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 @@ -138,6 +138,7 @@ public: uint64_t get_factor(); bool load_settings(); + int commit_settings(); /** * diff --git a/DSView/pv/view/groupsignal.cpp b/DSView/pv/view/groupsignal.cpp index aec24c5b..b48eefe5 100644 --- a/DSView/pv/view/groupsignal.cpp +++ b/DSView/pv/view/groupsignal.cpp @@ -2,8 +2,7 @@ * This file is part of the DSView project. * DSView is based on PulseView. * - * Copyright (C) 2012 Joel Holdsworth - * Copyright (C) 2013 DreamSourceLab + * Copyright (C) 2013 DreamSourceLab * * 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 diff --git a/DSView/pv/view/groupsignal.h b/DSView/pv/view/groupsignal.h index 0aa18282..711382ca 100644 --- a/DSView/pv/view/groupsignal.h +++ b/DSView/pv/view/groupsignal.h @@ -2,8 +2,7 @@ * This file is part of the DSView project. * DSView is based on PulseView. * - * Copyright (C) 2012 Joel Holdsworth - * Copyright (C) 2013 DreamSourceLab + * Copyright (C) 2013 DreamSourceLab * * 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 diff --git a/DSView/pv/view/header.cpp b/DSView/pv/view/header.cpp index 2eb1d5fb..3b46006b 100644 --- a/DSView/pv/view/header.cpp +++ b/DSView/pv/view/header.cpp @@ -3,7 +3,7 @@ * DSView is based on PulseView. * * Copyright (C) 2012 Joel Holdsworth - * Copyright (C) 2013 DreamSourceLab + * Copyright (C) 2013 DreamSourceLab * * 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 diff --git a/DSView/pv/view/header.h b/DSView/pv/view/header.h index 89d637ef..6d11b2fd 100644 --- a/DSView/pv/view/header.h +++ b/DSView/pv/view/header.h @@ -3,7 +3,7 @@ * DSView is based on PulseView. * * Copyright (C) 2012 Joel Holdsworth - * Copyright (C) 2013 DreamSourceLab + * Copyright (C) 2013 DreamSourceLab * * 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 diff --git a/DSView/pv/view/logicsignal.cpp b/DSView/pv/view/logicsignal.cpp index 09a43232..cff3801c 100644 --- a/DSView/pv/view/logicsignal.cpp +++ b/DSView/pv/view/logicsignal.cpp @@ -3,7 +3,7 @@ * DSView is based on PulseView. * * Copyright (C) 2012 Joel Holdsworth - * Copyright (C) 2013 DreamSourceLab + * Copyright (C) 2013 DreamSourceLab * * 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 diff --git a/DSView/pv/view/logicsignal.h b/DSView/pv/view/logicsignal.h index 3daa567b..ac64b1b8 100644 --- a/DSView/pv/view/logicsignal.h +++ b/DSView/pv/view/logicsignal.h @@ -3,7 +3,7 @@ * DSView is based on PulseView. * * Copyright (C) 2012 Joel Holdsworth - * Copyright (C) 2013 DreamSourceLab + * Copyright (C) 2013 DreamSourceLab * * 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 diff --git a/DSView/pv/view/mathtrace.cpp b/DSView/pv/view/mathtrace.cpp index 459c15a9..0abafb18 100644 --- a/DSView/pv/view/mathtrace.cpp +++ b/DSView/pv/view/mathtrace.cpp @@ -1,7 +1,7 @@ /* * This file is part of the PulseView project. * - * Copyright (C) 2012 Joel Holdsworth + * Copyright (C) 2016 DreamSourceLab * * 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 diff --git a/DSView/pv/view/mathtrace.h b/DSView/pv/view/mathtrace.h index 7a39babb..7fac9552 100644 --- a/DSView/pv/view/mathtrace.h +++ b/DSView/pv/view/mathtrace.h @@ -1,7 +1,7 @@ /* * This file is part of the PulseView project. * - * Copyright (C) 2012 Joel Holdsworth + * Copyright (C) 2016 DreamSourceLab * * 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 diff --git a/DSView/pv/view/ruler.cpp b/DSView/pv/view/ruler.cpp index 209a75a1..f6050a73 100644 --- a/DSView/pv/view/ruler.cpp +++ b/DSView/pv/view/ruler.cpp @@ -3,7 +3,7 @@ * DSView is based on PulseView. * * Copyright (C) 2012 Joel Holdsworth - * Copyright (C) 2013 DreamSourceLab + * Copyright (C) 2013 DreamSourceLab * * 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 diff --git a/DSView/pv/view/ruler.h b/DSView/pv/view/ruler.h index d3389bdc..41f415a6 100644 --- a/DSView/pv/view/ruler.h +++ b/DSView/pv/view/ruler.h @@ -3,7 +3,7 @@ * DSView is based on PulseView. * * Copyright (C) 2012 Joel Holdsworth - * Copyright (C) 2013 DreamSourceLab + * Copyright (C) 2013 DreamSourceLab * * 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 diff --git a/DSView/pv/view/selectableitem.cpp b/DSView/pv/view/selectableitem.cpp index 2b4b4059..1c4d987e 100644 --- a/DSView/pv/view/selectableitem.cpp +++ b/DSView/pv/view/selectableitem.cpp @@ -3,7 +3,7 @@ * DSView is based on PulseView. * * Copyright (C) 2013 Joel Holdsworth - * Copyright (C) 2014 DreamSourceLab + * Copyright (C) 2014 DreamSourceLab * * 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 diff --git a/DSView/pv/view/selectableitem.h b/DSView/pv/view/selectableitem.h index 28004a2d..de9998a9 100644 --- a/DSView/pv/view/selectableitem.h +++ b/DSView/pv/view/selectableitem.h @@ -3,7 +3,7 @@ * DSView is based on PulseView. * * Copyright (C) 2013 Joel Holdsworth - * Copyright (C) 2014 DreamSourceLab + * Copyright (C) 2014 DreamSourceLab * * 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 diff --git a/DSView/pv/view/signal.cpp b/DSView/pv/view/signal.cpp index 04a4e6ed..9abd0ce9 100644 --- a/DSView/pv/view/signal.cpp +++ b/DSView/pv/view/signal.cpp @@ -3,7 +3,7 @@ * DSView is based on PulseView. * * Copyright (C) 2012 Joel Holdsworth - * Copyright (C) 2013 DreamSourceLab + * Copyright (C) 2013 DreamSourceLab * * 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 diff --git a/DSView/pv/view/signal.h b/DSView/pv/view/signal.h index 632f1e37..01fabb25 100644 --- a/DSView/pv/view/signal.h +++ b/DSView/pv/view/signal.h @@ -3,7 +3,7 @@ * DSView is based on PulseView. * * Copyright (C) 2012 Joel Holdsworth - * Copyright (C) 2013 DreamSourceLab + * Copyright (C) 2013 DreamSourceLab * * 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 diff --git a/DSView/pv/view/timemarker.cpp b/DSView/pv/view/timemarker.cpp index bf4f9c87..1956f013 100644 --- a/DSView/pv/view/timemarker.cpp +++ b/DSView/pv/view/timemarker.cpp @@ -3,7 +3,7 @@ * DSView is based on PulseView. * * Copyright (C) 2012 Joel Holdsworth - * Copyright (C) 2013 DreamSourceLab + * Copyright (C) 2013 DreamSourceLab * * 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 diff --git a/DSView/pv/view/timemarker.h b/DSView/pv/view/timemarker.h index b6de934f..71ee4cfe 100644 --- a/DSView/pv/view/timemarker.h +++ b/DSView/pv/view/timemarker.h @@ -3,7 +3,7 @@ * DSView is based on PulseView. * * Copyright (C) 2012 Joel Holdsworth - * Copyright (C) 2013 DreamSourceLab + * Copyright (C) 2013 DreamSourceLab * * 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 diff --git a/DSView/pv/view/trace.cpp b/DSView/pv/view/trace.cpp index 661f59f5..c9971bc8 100644 --- a/DSView/pv/view/trace.cpp +++ b/DSView/pv/view/trace.cpp @@ -3,7 +3,7 @@ * DSView is based on PulseView. * * Copyright (C) 2013 Joel Holdsworth - * Copyright (C) 2014 DreamSourceLab + * Copyright (C) 2014 DreamSourceLab * * 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 diff --git a/DSView/pv/view/trace.h b/DSView/pv/view/trace.h index 1c30de77..2dd9662c 100644 --- a/DSView/pv/view/trace.h +++ b/DSView/pv/view/trace.h @@ -3,7 +3,7 @@ * DSView is based on PulseView. * * Copyright (C) 2013 Joel Holdsworth - * Copyright (C) 2014 DreamSourceLab + * Copyright (C) 2014 DreamSourceLab * * 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 diff --git a/DSView/pv/view/view.cpp b/DSView/pv/view/view.cpp index d97d4759..c3e2193d 100644 --- a/DSView/pv/view/view.cpp +++ b/DSView/pv/view/view.cpp @@ -3,7 +3,7 @@ * DSView is based on PulseView. * * Copyright (C) 2012 Joel Holdsworth - * Copyright (C) 2013 DreamSourceLab + * Copyright (C) 2013 DreamSourceLab * * 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 @@ -47,6 +47,7 @@ #include "pv/sigsession.h" #include "pv/data/logic.h" #include "pv/data/logicsnapshot.h" +#include "pv/dialogs/calibration.h" using namespace boost; using namespace std; @@ -179,6 +180,9 @@ View::View(SigSession &session, pv::toolbars::SamplingBar *sampling_bar, QWidget _show_search_cursor = false; _search_pos = 0; _search_cursor = new Cursor(*this, Trace::dsLightBlue, _search_pos); + + _cali = new pv::dialogs::Calibration(this); + _cali->hide(); } SigSession& View::session() @@ -972,6 +976,25 @@ double View::get_max_offset() - _scale * (get_view_width() * MaxViewRate); } +// -- calibration dialog +void View::show_calibration() +{ + _cali->set_device(_session.get_device()); + _cali->show(); +} + +void View::hide_calibration() +{ + _cali->hide(); +} + +void View::update_calibration() +{ + if (_cali->isVisible()) { + _cali->set_device(_session.get_device()); + } +} + QString View::trigger_time() { return _trigger_time.toString("yyyy-MM-dd hh:mm:ss ddd"); diff --git a/DSView/pv/view/view.h b/DSView/pv/view/view.h index 028eee48..55f8ae91 100644 --- a/DSView/pv/view/view.h +++ b/DSView/pv/view/view.h @@ -3,7 +3,7 @@ * DSView is based on PulseView. * * Copyright (C) 2012 Joel Holdsworth - * Copyright (C) 2013 DreamSourceLab + * Copyright (C) 2013 DreamSourceLab * * 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 @@ -231,6 +231,8 @@ public slots: void data_updated(); void update_scale_offset(); void show_region(uint64_t start, uint64_t end); + // -- calibration + void update_calibration(); private slots: @@ -245,6 +247,9 @@ private slots: void set_trig_pos(quint64 trig_pos); + // calibration for oscilloscope + void show_calibration(); + void hide_calibration(); void on_measure_updated(); void splitterMoved(int pos, int index); @@ -292,7 +297,7 @@ private: uint64_t _search_pos; QPointF _hover_point; - + dialogs::Calibration *_cali; QDateTime _trigger_time; }; diff --git a/DSView/pv/view/viewport.cpp b/DSView/pv/view/viewport.cpp index 9197f0ce..07f0dee8 100644 --- a/DSView/pv/view/viewport.cpp +++ b/DSView/pv/view/viewport.cpp @@ -3,7 +3,7 @@ * DSView is based on PulseView. * * Copyright (C) 2012 Joel Holdsworth - * Copyright (C) 2013 DreamSourceLab + * Copyright (C) 2013 DreamSourceLab * * 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 diff --git a/DSView/pv/view/viewport.h b/DSView/pv/view/viewport.h index 33ed45b2..cee1e9eb 100644 --- a/DSView/pv/view/viewport.h +++ b/DSView/pv/view/viewport.h @@ -3,7 +3,7 @@ * DSView is based on PulseView. * * Copyright (C) 2012 Joel Holdsworth - * Copyright (C) 2013 DreamSourceLab + * Copyright (C) 2013 DreamSourceLab * * 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 diff --git a/DSView/pv/widgets/decodergroupbox.cpp b/DSView/pv/widgets/decodergroupbox.cpp index d03082e6..4be51822 100644 --- a/DSView/pv/widgets/decodergroupbox.cpp +++ b/DSView/pv/widgets/decodergroupbox.cpp @@ -2,6 +2,7 @@ * This file is part of the PulseView project. * * Copyright (C) 2013 Joel Holdsworth + * Copyright (C) 2016 DreamSourceLab * * 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 diff --git a/DSView/pv/widgets/decodergroupbox.h b/DSView/pv/widgets/decodergroupbox.h index c69543d5..d780241b 100644 --- a/DSView/pv/widgets/decodergroupbox.h +++ b/DSView/pv/widgets/decodergroupbox.h @@ -2,6 +2,7 @@ * This file is part of the PulseView project. * * Copyright (C) 2013 Joel Holdsworth + * Copyright (C) 2016 DreamSourceLab * * 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 diff --git a/DSView/pv/widgets/fakelineedit.cpp b/DSView/pv/widgets/fakelineedit.cpp index 84e0c278..d8a2930a 100644 --- a/DSView/pv/widgets/fakelineedit.cpp +++ b/DSView/pv/widgets/fakelineedit.cpp @@ -2,8 +2,7 @@ * This file is part of the DSView project. * DSView is based on PulseView. * - * Copyright (C) 2012 Joel Holdsworth - * Copyright (C) 2013 DreamSourceLab + * Copyright (C) 2013 DreamSourceLab * * 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 diff --git a/DSView/pv/widgets/fakelineedit.h b/DSView/pv/widgets/fakelineedit.h index a274f990..071c59e4 100644 --- a/DSView/pv/widgets/fakelineedit.h +++ b/DSView/pv/widgets/fakelineedit.h @@ -2,8 +2,7 @@ * This file is part of the DSView project. * DSView is based on PulseView. * - * Copyright (C) 2012 Joel Holdsworth - * Copyright (C) 2013 DreamSourceLab + * Copyright (C) 2013 DreamSourceLab * * 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 diff --git a/libsigrok4DSL/hardware/DSL/command.c b/libsigrok4DSL/hardware/DSL/command.c index accf2f15..e65741ed 100644 --- a/libsigrok4DSL/hardware/DSL/command.c +++ b/libsigrok4DSL/hardware/DSL/command.c @@ -234,19 +234,19 @@ SR_PRIV int command_get_status(libusb_device_handle *devhdl, return SR_OK; } -SR_PRIV int command_vth(libusb_device_handle *devhdl, double vth) +SR_PRIV int command_wr_reg(libusb_device_handle *devhdl, uint8_t value, uint8_t addr) { int ret; - uint8_t cmd; + uint16_t cmd; - cmd = vth/5.0 * 255; + cmd = value + (addr << 8); /* Send the control command. */ ret = libusb_control_transfer(devhdl, LIBUSB_REQUEST_TYPE_VENDOR | - LIBUSB_ENDPOINT_OUT, CMD_VTH, 0x0000, 0x0000, + LIBUSB_ENDPOINT_OUT, CMD_WR_REG, 0x0000, 0x0000, (unsigned char *)&cmd, sizeof(cmd), 3000); if (ret < 0) { - sr_err("Unable to send VTH command: %s.", - libusb_error_name(ret)); + sr_err("Unable to write REG @ address %d : %s.", + addr, libusb_error_name(ret)); return SR_ERR; } @@ -276,7 +276,7 @@ SR_PRIV int command_rd_nvm(libusb_device_handle *devhdl, unsigned char *ctx, uin int ret; struct cmd_nvm_info nvm_info; - assert(len <= 8); + assert(len <= 32); nvm_info.addr = addr; nvm_info.len = len; diff --git a/libsigrok4DSL/hardware/DSL/command.h b/libsigrok4DSL/hardware/DSL/command.h index 5f9735a0..44485e24 100644 --- a/libsigrok4DSL/hardware/DSL/command.h +++ b/libsigrok4DSL/hardware/DSL/command.h @@ -33,7 +33,7 @@ #define CMD_CONTROL 0xb5 #define CMD_STATUS 0xb6 #define CMD_STATUS_INFO 0xb7 -#define CMD_VTH 0xb8 +#define CMD_WR_REG 0xb8 #define CMD_WR_NVM 0xb9 #define CMD_RD_NVM 0xba #define CMD_RD_NVM_PRE 0xbb @@ -55,6 +55,10 @@ #define CMD_STATUS_CNT 32 +#define VTH_ADDR 0x78 +#define EEWP_ADDR 0x70 +#define COMB_ADDR 0x68 + #pragma pack(push, 1) struct version_info { @@ -98,22 +102,38 @@ struct cmd_status_info { struct cmd_zero_info { uint8_t zero_addr; - uint8_t vpos_l; - uint8_t vpos_h; - uint8_t voff_l; - uint8_t voff_h; - uint8_t vcntr_l; - uint8_t vcntr_h; - uint8_t adc_off; + uint8_t voff0; + uint8_t voff1; + uint8_t voff2; + uint8_t voff3; + uint8_t voff4; + uint8_t voff5; + uint8_t voff6; + uint8_t voff7; + uint8_t voff8; + uint8_t voff9; + uint8_t voff10; + uint8_t voff11; + uint8_t voff12; + uint8_t voff13; + uint8_t voff14; + uint8_t voff15; + uint8_t diff0; + uint8_t diff1; + uint8_t trans0; + uint8_t trans1; }; -struct cmd_comb_info { - uint8_t comb_addr; - uint8_t comb0_low_off; - uint8_t comb0_hig_off; - uint8_t comb1_low_off; - uint8_t comb1_hig_off; - uint8_t comb_sign; +struct cmd_vga_info { + uint8_t vga_addr; + uint16_t vga0; + uint16_t vga1; + uint16_t vga2; + uint16_t vga3; + uint16_t vga4; + uint16_t vga5; + uint16_t vga6; + uint16_t vga7; }; struct cmd_nvm_info { @@ -140,7 +160,7 @@ SR_PRIV int command_get_status(libusb_device_handle *devhdl, unsigned char *status, int begin, int end); -SR_PRIV int command_vth(libusb_device_handle *devhdl, double vth); +SR_PRIV int command_wr_reg(libusb_device_handle *devhdl, uint8_t value, uint8_t addr); SR_PRIV int command_wr_nvm(libusb_device_handle *devhdl, unsigned char *ctx, uint8_t len); SR_PRIV int command_rd_nvm(libusb_device_handle *devhdl, unsigned char *ctx, uint8_t addr, uint8_t len); diff --git a/libsigrok4DSL/hardware/DSL/dscope.c b/libsigrok4DSL/hardware/DSL/dscope.c index 26191168..ef4f8dd8 100644 --- a/libsigrok4DSL/hardware/DSL/dscope.c +++ b/libsigrok4DSL/hardware/DSL/dscope.c @@ -139,8 +139,7 @@ static const uint64_t samplecounts[] = { SR_MB(32), }; -static const uint8_t zero_base_addr = 0x80; -static const uint8_t comb_base_addr = 0xB0; +static const uint8_t zero_base_addr = 0x40; SR_PRIV struct sr_dev_driver DSCope_driver_info; static struct sr_dev_driver *di = &DSCope_driver_info; @@ -149,8 +148,51 @@ extern struct ds_trigger *trigger; gboolean mstatus_valid = FALSE; struct sr_status mstatus; -struct cmd_zero_info zero_info; -struct cmd_comb_info comb_info; + +static const uint64_t DSCOPE_DEFAULT_VGAIN[] = { + DSCOPE_DEFAULT_VGAIN0, + DSCOPE_DEFAULT_VGAIN1, + DSCOPE_DEFAULT_VGAIN2, + DSCOPE_DEFAULT_VGAIN3, + DSCOPE_DEFAULT_VGAIN4, + DSCOPE_DEFAULT_VGAIN5, + DSCOPE_DEFAULT_VGAIN6, + DSCOPE_DEFAULT_VGAIN7, +}; + +static const uint64_t DSCOPE20_DEFAULT_VGAIN[] = { + DSCOPE20_DEFAULT_VGAIN0, + DSCOPE20_DEFAULT_VGAIN1, + DSCOPE20_DEFAULT_VGAIN2, + DSCOPE20_DEFAULT_VGAIN3, + DSCOPE20_DEFAULT_VGAIN4, + DSCOPE20_DEFAULT_VGAIN5, + DSCOPE20_DEFAULT_VGAIN6, + DSCOPE20_DEFAULT_VGAIN7, +}; + +struct DSL_vga DSCope_vga[] = { + {10, DSCOPE_DEFAULT_VGAIN0, DSCOPE_DEFAULT_VGAIN0, DSCOPE_DEFAULT_VOFF, DSCOPE_DEFAULT_VOFF}, + {20, DSCOPE_DEFAULT_VGAIN1, DSCOPE_DEFAULT_VGAIN1, DSCOPE_DEFAULT_VOFF, DSCOPE_DEFAULT_VOFF}, + {50, DSCOPE_DEFAULT_VGAIN2, DSCOPE_DEFAULT_VGAIN2, DSCOPE_DEFAULT_VOFF, DSCOPE_DEFAULT_VOFF}, + {100, DSCOPE_DEFAULT_VGAIN3, DSCOPE_DEFAULT_VGAIN3, DSCOPE_DEFAULT_VOFF, DSCOPE_DEFAULT_VOFF}, + {200, DSCOPE_DEFAULT_VGAIN4, DSCOPE_DEFAULT_VGAIN4, DSCOPE_DEFAULT_VOFF, DSCOPE_DEFAULT_VOFF}, + {500, DSCOPE_DEFAULT_VGAIN5, DSCOPE_DEFAULT_VGAIN5, DSCOPE_DEFAULT_VOFF, DSCOPE_DEFAULT_VOFF}, + {1000,DSCOPE_DEFAULT_VGAIN6, DSCOPE_DEFAULT_VGAIN6, DSCOPE_DEFAULT_VOFF, DSCOPE_DEFAULT_VOFF}, + {2000,DSCOPE_DEFAULT_VGAIN7, DSCOPE_DEFAULT_VGAIN7, DSCOPE_DEFAULT_VOFF, DSCOPE_DEFAULT_VOFF}, + {0, 0, 0, 0}, +}; +struct DSL_vga DSCope20_vga[] = { + {10, DSCOPE20_DEFAULT_VGAIN0, DSCOPE20_DEFAULT_VGAIN0, DSCOPE20_DEFAULT_VOFF, CALI_VOFF_RANGE-DSCOPE20_DEFAULT_VOFF}, + {20, DSCOPE20_DEFAULT_VGAIN1, DSCOPE20_DEFAULT_VGAIN1, DSCOPE20_DEFAULT_VOFF, CALI_VOFF_RANGE-DSCOPE20_DEFAULT_VOFF}, + {50, DSCOPE20_DEFAULT_VGAIN2, DSCOPE20_DEFAULT_VGAIN2, DSCOPE20_DEFAULT_VOFF, CALI_VOFF_RANGE-DSCOPE20_DEFAULT_VOFF}, + {100, DSCOPE20_DEFAULT_VGAIN3, DSCOPE20_DEFAULT_VGAIN3, DSCOPE20_DEFAULT_VOFF, CALI_VOFF_RANGE-DSCOPE20_DEFAULT_VOFF}, + {200, DSCOPE20_DEFAULT_VGAIN4, DSCOPE20_DEFAULT_VGAIN4, DSCOPE20_DEFAULT_VOFF, CALI_VOFF_RANGE-DSCOPE20_DEFAULT_VOFF}, + {500, DSCOPE20_DEFAULT_VGAIN5, DSCOPE20_DEFAULT_VGAIN5, DSCOPE20_DEFAULT_VOFF, CALI_VOFF_RANGE-DSCOPE20_DEFAULT_VOFF}, + {1000,DSCOPE20_DEFAULT_VGAIN6, DSCOPE20_DEFAULT_VGAIN6, DSCOPE20_DEFAULT_VOFF, CALI_VOFF_RANGE-DSCOPE20_DEFAULT_VOFF}, + {2000,DSCOPE20_DEFAULT_VGAIN7, DSCOPE20_DEFAULT_VGAIN7, DSCOPE20_DEFAULT_VOFF, CALI_VOFF_RANGE-DSCOPE20_DEFAULT_VOFF}, + {0, 0, 0, 0}, +}; /** * Check the USB configuration to determine if this is an DSCope device. @@ -162,7 +204,7 @@ static gboolean check_conf_profile(libusb_device *dev) { struct libusb_device_descriptor des; struct libusb_device_handle *hdl; - gboolean ret; + gboolean ret; unsigned char strdesc[64]; hdl = NULL; @@ -184,7 +226,7 @@ static gboolean check_conf_profile(libusb_device *dev) if (libusb_get_string_descriptor_ascii(hdl, des.iProduct, strdesc, sizeof(strdesc)) < 0) break; - if (strncmp((const char *)strdesc, "DSCope", 6)) + if (strncmp((const char *)strdesc, "USB-based Instrument", 20)) break; /* If we made it here, it must be an DSCope. */ @@ -261,8 +303,8 @@ static int fpga_setting(const struct sr_dev_inst *sdi) ((devc->cur_samplerate == SR_MHZ(400)) << 6) + ((sdi->mode == ANALOG) << 7) + ((devc->filter == SR_FILTER_1T) << 8) + - (devc->instant << 9) + (devc->zero << 10); - setting.divider = devc->zero ? 0x1 : (uint32_t)ceil(DSCOPE_MAX_SAMPLERATE * 1.0 / devc->cur_samplerate / channel_en_cnt); + (devc->instant << 9); + setting.divider = (uint32_t)ceil(DSCOPE_MAX_SAMPLERATE * 1.0 / devc->cur_samplerate / channel_en_cnt); setting.count = (uint32_t)(devc->limit_samples / (channel_cnt / channel_en_cnt)); setting.trig_pos = (uint32_t)(trigger->trigger_pos / 100.0 * devc->limit_samples); setting.trig_glb = trigger->trigger_stages; @@ -592,6 +634,7 @@ static struct DSL_context *DSCope_dev_new(void) devc->trigger_hrate = 0; devc->zero = FALSE; devc->data_lock = FALSE; + devc->cali = FALSE; return devc; } @@ -606,6 +649,74 @@ static int init(struct sr_context *sr_ctx) return std_hw_init(sr_ctx, di, LOG_PREFIX); } + +static struct DSL_vga* get_vga_ptr(struct sr_dev_inst *sdi) +{ + struct DSL_vga *vga_ptr = NULL; + if (strcmp(sdi->model, "DSCope") == 0) + vga_ptr = DSCope_vga; + else if (strcmp(sdi->model, "DSCope20") == 0) + vga_ptr = DSCope20_vga; + + return vga_ptr; +} + +static uint16_t get_default_trans(struct sr_dev_inst *sdi) +{ + uint16_t trans = 1; + if (strcmp(sdi->model, "DSCope") == 0) + trans = DSCOPE_DEFAULT_TRANS; + else if (strcmp(sdi->model, "DSCope20") == 0) + trans = DSCOPE20_DEFAULT_TRANS; + + return trans; +} + +static uint16_t get_default_voff(struct sr_dev_inst *sdi, int ch_index) +{ + uint16_t voff = 0; + if (strcmp(sdi->model, "DSCope") == 0) + voff = DSCOPE_DEFAULT_VOFF; + else if (strcmp(sdi->model, "DSCope20") == 0) + if (ch_index == 1) + voff = CALI_VOFF_RANGE - DSCOPE20_DEFAULT_VOFF; + else + voff = DSCOPE20_DEFAULT_VOFF; + + return voff; +} + +static uint64_t get_default_vgain(struct sr_dev_inst *sdi, int num) +{ + uint64_t vgain = 0; + if (strcmp(sdi->model, "DSCope") == 0) { + assert(num < sizeof(DSCOPE_DEFAULT_VGAIN)); + vgain = DSCOPE_DEFAULT_VGAIN[num]; + } + else if (strcmp(sdi->model, "DSCope20") == 0) { + assert(num < sizeof(DSCOPE20_DEFAULT_VGAIN)); + vgain = DSCOPE20_DEFAULT_VGAIN[num]; + } + + return vgain; +} + +static int probe_init(struct sr_dev_inst *sdi) +{ + GList *l; + for (l = sdi->channels; l; l = l->next) { + struct sr_channel *probe = (struct sr_channel *)l->data; + if (sdi->mode == DSO) { + probe->vdiv = 1000; + probe->vfactor = 1; + probe->vpos = 0; + probe->coupling = SR_DC_COUPLING; + probe->trig_value = 0x80; + probe->vpos_trans = get_default_trans(sdi); + } + } +} + static int set_probes(struct sr_dev_inst *sdi, int num_probes) { uint16_t j; @@ -615,15 +726,9 @@ static int set_probes(struct sr_dev_inst *sdi, int num_probes) if (!(probe = sr_channel_new(j, (sdi->mode == LOGIC) ? SR_CHANNEL_LOGIC : ((sdi->mode == DSO) ? SR_CHANNEL_DSO : SR_CHANNEL_ANALOG), TRUE, probe_names[j]))) return SR_ERR; - if (sdi->mode == DSO) { - probe->vdiv = 1000; - probe->vfactor = 1; - probe->vpos = 0; - probe->coupling = SR_DC_COUPLING; - probe->trig_value = 0x80; - } sdi->channels = g_slist_append(sdi->channels, probe); } + probe_init(sdi); return SR_OK; } @@ -785,15 +890,66 @@ static GSList *dev_mode_list(const struct sr_dev_inst *sdi) return l; } +static uint64_t dso_vga(struct sr_dev_inst *sdi, struct sr_channel* ch) +{ + int i; + struct DSL_vga *vga_ptr = get_vga_ptr(sdi); + for (i = 0; vga_ptr && (vga_ptr+i)->key; i++) { + if ((vga_ptr+i)->key == ch->vdiv) + return (ch->index == 0) ? (vga_ptr+i)->vgain0 : (vga_ptr+i)->vgain1; + } + + return 0; +} + +static uint64_t dso_voff(struct sr_dev_inst *sdi, struct sr_channel* ch) +{ + int i; + struct DSL_vga *vga_ptr = get_vga_ptr(sdi); + for (i = 0; vga_ptr && (vga_ptr+i)->key; i++) { + if ((vga_ptr+i)->key == ch->vdiv) + return (ch->index == 0) ? (vga_ptr+i)->voff0 : (vga_ptr+i)->voff1; + } + return 0; +} + +static uint64_t dso_vpos(struct sr_dev_inst *sdi, struct sr_channel* ch) +{ + uint64_t vpos; + int vpos_coarse, vpos_fine; + int trans_coarse, trans_fine; + struct DSL_context *devc = sdi->priv; + const double voltage = (devc->zero && devc->zero_comb == -1) ? 0 : ch->vpos; + if (strcmp(sdi->model, "DSCope") == 0) { + trans_coarse = (ch->vpos_trans & 0xFF00) >> 8; + trans_fine = (ch->vpos_trans & 0x00FF); + if (ch->vdiv < 500) { + vpos_coarse = floor(-voltage*DSCOPE_TRANS_CMULTI/trans_coarse + 0.5); + vpos_fine = floor((voltage + vpos_coarse*trans_coarse/DSCOPE_TRANS_CMULTI)*1000.0/trans_fine + 0.5); + } else { + vpos_coarse = floor(-voltage/trans_coarse + 0.5); + vpos_fine = floor((voltage + vpos_coarse*trans_coarse)*DSCOPE_TRANS_FMULTI/trans_fine + 0.5); + } + //vpos = (vpos_coarse << 16) + vpos_fine; + } else if (strcmp(sdi->model, "DSCope20") == 0) { + vpos = ((ch->vdiv*5.0) - voltage)/(ch->vdiv*10.0)*ch->vpos_trans; + } + + const uint64_t voff = dso_voff(sdi, ch); + if (strcmp(sdi->model, "DSCope") == 0) + return ((vpos_coarse+DSCOPE_CONSTANT_BIAS+(voff>>10)) << 16)+vpos_fine+(voff&0x03ff); + else if (strcmp(sdi->model, "DSCope20") == 0) + return vpos+voff; + else + return 0; +} + static uint64_t dso_cmd_gen(struct sr_dev_inst *sdi, struct sr_channel* ch, int id) { struct DSL_context *devc; uint64_t cmd = 0; int channel_cnt = 0; - uint16_t vpos_coarse; - uint16_t vpos_fine; - gboolean vpos_coarse_neg; - gboolean vpos_fine_neg; + uint64_t vpos; GSList *l; const int ch_bit = 7; @@ -806,59 +962,36 @@ static uint64_t dso_cmd_gen(struct sr_dev_inst *sdi, struct sr_channel* ch, int struct sr_channel *probe = (struct sr_channel *)l->data; channel_cnt += probe->enabled; } - if (channel_cnt == 1) { + if (devc->zero || channel_cnt == 2) { + cmd += 0x0E00; + //cmd += 0x000; + } else if (channel_cnt == 1) { if (((ch->index == 0) && ch->enabled) || ((ch->index == 1) && !ch->enabled)) cmd += 0x1600; else if (((ch->index == 1) && ch->enabled) || ((ch->index == 0) && !ch->enabled)) cmd += 0x1A00; - } else if (channel_cnt == 2) { - cmd += 0x0E00; - //cmd += 0x000; } else { return 0x0; } cmd += ch->index << ch_bit; - if (ch->coupling == SR_GND_COUPLING) - cmd &= 0xFFFFFDFF; - else if (ch->coupling == SR_DC_COUPLING) + if (devc->zero || ch->coupling == SR_DC_COUPLING) cmd += 0x100; + else if (ch->coupling == SR_GND_COUPLING) + cmd &= 0xFFFFFDFF; break; case SR_CONF_VDIV: case SR_CONF_TIMEBASE: cmd += 0x8; cmd += ch->index << ch_bit; - // --VDBS - switch(ch->vdiv){ - case 5: cmd += 0x170000; break; - case 10: cmd += 0x162800; break; - case 20: cmd += 0x14D000; break; - case 50: cmd += 0x12E800; break; - case 100: cmd += 0x118000; break; - case 200: cmd += 0x101800; break; - case 500: cmd += 0x2E800; break; - case 1000: cmd += 0x18000; break; - case 2000: cmd += 0x01800; break; - case 5000: cmd += 0x00000; break; - default: cmd += 0x0; break; - } + // --VGAIN + cmd += dso_vga(sdi, ch); break; case SR_CONF_VPOS: cmd += 0x10; cmd += ch->index << ch_bit; - if (ch->vdiv < 500) { - vpos_coarse_neg = (ch->vpos < 0); - vpos_coarse = (uint16_t)(abs(ch->vpos)/(2*VPOS_STEP) + 0.5) * 4; - vpos_fine_neg = vpos_coarse_neg ^ ((abs(ch->vpos) < vpos_coarse*0.5*VPOS_STEP)); - vpos_fine = (uint16_t)(abs((abs(ch->vpos) - vpos_coarse*0.5*VPOS_STEP))/(2*VPOS_MINISTEP) + 0.5); - } else { - vpos_coarse_neg = (ch->vpos < 0); - vpos_coarse = (uint16_t)(abs(ch->vpos)/(20*VPOS_STEP) + 0.5) * 4; - vpos_fine_neg = vpos_coarse_neg ^ ((abs(ch->vpos) < vpos_coarse*5*VPOS_STEP)); - vpos_fine = (uint16_t)(abs((abs(ch->vpos) - vpos_coarse*5*VPOS_STEP))/(20*VPOS_MINISTEP) + 0.5); - } - cmd += (vpos_fine_neg << 31) + (vpos_fine << 20) + - (vpos_coarse_neg << 19) + (vpos_coarse << 8); + vpos = dso_vpos(sdi, ch); + cmd += (vpos << 8); break; case SR_CONF_SAMPLERATE: for (l = sdi->channels; l; l = l->next) { @@ -879,7 +1012,7 @@ static uint64_t dso_cmd_gen(struct sr_dev_inst *sdi, struct sr_channel* ch, int break; case SR_CONF_TRIGGER_SOURCE: cmd += 0x30; - cmd += devc->trigger_source << 8; + cmd += devc->zero ? 0x0 : devc->trigger_source << 8; break; case SR_CONF_TRIGGER_VALUE: cmd += 0x38; @@ -888,28 +1021,6 @@ static uint64_t dso_cmd_gen(struct sr_dev_inst *sdi, struct sr_channel* ch, int cmd += probe->trig_value << (8 * (probe->index + 1)); } break; - case SR_CONF_ZERO_SET: - cmd += 0x40; - cmd += ch->index << ch_bit; - cmd += ((uint64_t)zero_info.vpos_l << 8); - cmd += ((uint64_t)(zero_info.vpos_h & 0x3) << 16); - cmd += ((uint64_t)zero_info.voff_l << 24); - cmd += ((uint64_t)(zero_info.voff_h & 0x3) << 32); - cmd += ((uint64_t)zero_info.vcntr_l << 40); - cmd += ((uint64_t)(zero_info.vcntr_h & 0x3) << 48); - cmd += ((uint64_t)zero_info.adc_off << 56); - break; - case SR_CONF_COMB_SET: - cmd += 0x48; - cmd += ((uint64_t)comb_info.comb0_low_off << 8); - cmd += ((uint64_t)comb_info.comb0_hig_off << 16); - cmd += ((uint64_t)comb_info.comb1_low_off << 24); - cmd += ((uint64_t)comb_info.comb1_hig_off << 32); - cmd += ((uint64_t)comb_info.comb_sign << 40); - break; - case SR_CONF_ZERO_OVER: - cmd += 0x50; - break; case SR_CONF_TRIGGER_HOLDOFF: cmd += 0x58; cmd += ((uint64_t)devc->trigger_holdoff << 8); @@ -918,214 +1029,72 @@ static uint64_t dso_cmd_gen(struct sr_dev_inst *sdi, struct sr_channel* ch, int cmd = 0xa5a5a500; break; default: - cmd = 0x00000000; + cmd = 0xFFFFFFFF; } return cmd; } -static int dev_open(struct sr_dev_inst *sdi) +static gboolean dso_load_eep(struct sr_dev_inst *sdi, struct sr_channel *probe) { - struct sr_usb_dev_inst *usb; - struct DSL_context *devc; - int ret; - int64_t timediff_us, timediff_ms; - - devc = sdi->priv; - usb = sdi->conn; - - /* - * If the firmware was recently uploaded, wait up to MAX_RENUM_DELAY_MS - * milliseconds for the FX2 to renumerate. - */ - ret = SR_ERR; - if (devc->fw_updated > 0) { - sr_info("Waiting for device to reset."); - /* Takes >= 300ms for the FX2 to be gone from the USB bus. */ - g_usleep(300 * 1000); - timediff_ms = 0; - while (timediff_ms < MAX_RENUM_DELAY_MS) { - if ((ret = DSCope_dev_open(sdi)) == SR_OK) - break; - g_usleep(100 * 1000); - - timediff_us = g_get_monotonic_time() - devc->fw_updated; - timediff_ms = timediff_us / 1000; - sr_spew("Waited %" PRIi64 "ms.", timediff_ms); - } - if (ret != SR_OK) { - sr_err("Device failed to renumerate."); - return SR_ERR; - } - sr_info("Device came back after %" PRIi64 "ms.", timediff_ms); - } else { - sr_info("Firmware upload was not needed."); - ret = DSCope_dev_open(sdi); - } - - if (ret != SR_OK) { - sr_err("Unable to open device."); - return SR_ERR; - } - - ret = libusb_claim_interface(usb->devhdl, USB_INTERFACE); - if (ret != 0) { - switch(ret) { - case LIBUSB_ERROR_BUSY: - sr_err("Unable to claim USB interface. Another " - "program or driver has already claimed it."); - break; - case LIBUSB_ERROR_NO_DEVICE: - sr_err("Device has been disconnected."); - break; - default: - sr_err("Unable to claim interface: %s.", - libusb_error_name(ret)); - break; - } - - return SR_ERR; - } - - if ((ret = command_fpga_config(usb->devhdl)) != SR_OK) { - sr_err("Send FPGA configure command failed!"); + int ret, i; + struct sr_usb_dev_inst *usb = sdi->conn; + struct cmd_zero_info zero_info; + uint8_t dst_addr = (zero_base_addr + + probe->index * (sizeof(struct cmd_zero_info) + sizeof(struct cmd_vga_info))); + zero_info.zero_addr = dst_addr; + if ((ret = command_rd_nvm(usb->devhdl, (unsigned char *)&zero_info, zero_info.zero_addr, sizeof(struct cmd_zero_info))) != SR_OK) { + return FALSE; + sr_err("Send Get Zero command failed!"); } else { - /* Takes >= 10ms for the FX2 to be ready for FPGA configure. */ - g_usleep(10 * 1000); - char *fpga_bit = malloc(strlen(config_path)+strlen(devc->profile->fpga_bit33)+1); - if (fpga_bit == NULL) - return SR_ERR_MALLOC; - strcpy(fpga_bit, config_path); - strcat(fpga_bit, devc->profile->fpga_bit33); - ret = fpga_config(usb->devhdl, fpga_bit); - if (ret != SR_OK) { - sr_err("Configure FPGA failed!"); - } - } - - if (sdi->mode == DSO) { - GSList *l; - for(l = sdi->channels; l; l = l->next) { - struct sr_channel *probe = (struct sr_channel *)l->data; - ret = command_dso_ctrl(usb->devhdl, dso_cmd_gen(sdi, probe, SR_CONF_COUPLING)); - if (ret != SR_OK) { - sr_err("DSO set coupling of channel %d command failed!", probe->index); - return ret; - } - ret = command_dso_ctrl(usb->devhdl, dso_cmd_gen(sdi, probe, SR_CONF_VDIV)); - if (ret != SR_OK) { - sr_err("Set VDIV of channel %d command failed!", probe->index); - return ret; - } - ret = command_dso_ctrl(usb->devhdl, dso_cmd_gen(sdi, probe, SR_CONF_VPOS)); - if (ret != SR_OK) { - sr_err("Set VDIV of channel %d command failed!", probe->index); - return ret; - } - } - ret = command_dso_ctrl(usb->devhdl, dso_cmd_gen(sdi, 0, SR_CONF_SAMPLERATE)); - if (ret != SR_OK) { - sr_err("Set Sample Rate command failed!"); - return ret; - } - ret = command_dso_ctrl(usb->devhdl, dso_cmd_gen(sdi, NULL, SR_CONF_HORIZ_TRIGGERPOS)); - if (ret != SR_OK) { - sr_err("Set Horiz Trigger Position command failed!"); - return ret; - } - ret = command_dso_ctrl(usb->devhdl, dso_cmd_gen(sdi, NULL, SR_CONF_TRIGGER_HOLDOFF)); - if (ret != SR_OK) { - sr_err("Set Trigger Holdoff Time command failed!"); - return ret; - } - ret = command_dso_ctrl(usb->devhdl, dso_cmd_gen(sdi, NULL, SR_CONF_TRIGGER_SLOPE)); - if (ret != SR_OK) { - sr_err("Set Trigger Slope command failed!"); - return ret; - } - ret = command_dso_ctrl(usb->devhdl, dso_cmd_gen(sdi, NULL, SR_CONF_TRIGGER_SOURCE)); - if (ret != SR_OK) { - sr_err("Set Trigger Source command failed!"); - return ret; - } - ret = command_dso_ctrl(usb->devhdl, dso_cmd_gen(sdi, NULL, SR_CONF_TRIGGER_VALUE)); - if (ret != SR_OK) { - sr_err("Set Trigger Value command failed!"); - return ret; - } - } - - GSList *l; - for(l = sdi->channels; l; l = l->next) { - struct sr_channel *probe = (struct sr_channel *)l->data; - zero_info.zero_addr = (zero_base_addr + probe->index * sizeof(struct cmd_zero_info)); - if ((ret = command_rd_nvm(usb->devhdl, (unsigned char *)&zero_info, zero_info.zero_addr, sizeof(struct cmd_zero_info))) != SR_OK) { - sr_err("Send Get Zero command failed!"); + if (zero_info.zero_addr == dst_addr) { + uint8_t* voff_ptr = &zero_info.zero_addr + 1; + struct DSL_vga *vga_ptr = get_vga_ptr(sdi); + for (i = 0; vga_ptr && (vga_ptr+i)->key; i++) { + if (probe->index == 0) + (vga_ptr+i)->voff0 = (*(voff_ptr + 2*i+1) << 8) + *(voff_ptr + 2*i); + else + (vga_ptr+i)->voff1 = (*(voff_ptr + 2*i+1) << 8) + *(voff_ptr + 2*i); + } + if (i != 0) { + probe->comb_diff_top = *(voff_ptr + 2*i); + probe->comb_diff_bom = *(voff_ptr + 2*i + 1); + probe->vpos_trans = *(voff_ptr + 2*i + 2) + (*(voff_ptr + 2*i + 3) << 8); + const double slope = (probe->comb_diff_bom - probe->comb_diff_top)/(2.0*255.0); + for (i = 0; i < 256; i++) { + ret = command_wr_reg(usb->devhdl, i, COMB_ADDR + probe->index*2); + int value = i+i*slope+probe->comb_diff_top*0.5+0.5; + value = (value < 0) ? 0 : + (value > 255) ? 255 : value; + ret = command_wr_reg(usb->devhdl, value, COMB_ADDR + probe->index*2 + 1); + } + } } else { - if (zero_info.zero_addr == (zero_base_addr + probe->index * sizeof(struct cmd_zero_info))) { - ret = command_dso_ctrl(usb->devhdl, dso_cmd_gen(sdi, probe, SR_CONF_ZERO_SET)); - if (ret != SR_OK) { - sr_err("Set Zero command failed!"); - return ret; - } - } else { - devc->zero = TRUE; - sr_info("Zero have not been setted!"); - } + return FALSE; } } - comb_info.comb_addr = comb_base_addr; - if ((ret = command_rd_nvm(usb->devhdl, (unsigned char *)&comb_info, comb_info.comb_addr, sizeof(struct cmd_comb_info))) != SR_OK) { - sr_err("Send Get Comb Command Failed!"); + struct cmd_vga_info vga_info; + vga_info.vga_addr = dst_addr + sizeof(struct cmd_zero_info); + if ((ret = command_rd_nvm(usb->devhdl, (unsigned char *)&vga_info, vga_info.vga_addr, sizeof(struct cmd_vga_info))) != SR_OK) { + return FALSE; + sr_err("Send Get Zero command failed!"); } else { - if (comb_info.comb_addr == comb_base_addr) { - ret = command_dso_ctrl(usb->devhdl, dso_cmd_gen(sdi, NULL, SR_CONF_COMB_SET)); - if (ret != SR_OK) { - sr_err("Set Comb command failed!"); - return ret; - } + if (vga_info.vga_addr == dst_addr + sizeof(struct cmd_zero_info)) { + uint16_t* vgain_ptr = &vga_info.vga0; + struct DSL_vga *vga_ptr = get_vga_ptr(sdi); + for (i = 0; vga_ptr && (vga_ptr+i)->key; i++) { + if (probe->index == 0) + (vga_ptr+i)->vgain0 = *(vgain_ptr + i) << 8; + else + (vga_ptr+i)->vgain1 = *(vgain_ptr + i) << 8; + } } else { - devc->zero = TRUE; - sr_info("Comb have not been setted!"); + return FALSE; } } - return SR_OK; -} - -static int dev_close(struct sr_dev_inst *sdi) -{ - struct sr_usb_dev_inst *usb; - - usb = sdi->conn; - if (usb->devhdl == NULL) - return SR_ERR; - - sr_info("DSCope: Closing device %d on %d.%d interface %d.", - sdi->index, usb->bus, usb->address, USB_INTERFACE); - libusb_release_interface(usb->devhdl, USB_INTERFACE); - libusb_close(usb->devhdl); - usb->devhdl = NULL; - sdi->status = SR_ST_INACTIVE; - - return SR_OK; -} - -static int cleanup(void) -{ - int ret; - struct drv_context *drvc; - - if (!(drvc = di->priv)) - return SR_OK; - - ret = dev_clear(); - - g_free(drvc); - di->priv = NULL; - - return ret; + return TRUE; } static int config_get(int id, GVariant **data, const struct sr_dev_inst *sdi, @@ -1134,22 +1103,24 @@ static int config_get(int id, GVariant **data, const struct sr_dev_inst *sdi, { struct DSL_context *devc; struct sr_usb_dev_inst *usb; - char str[128]; + char str[128]; + int i; + struct DSL_vga *vga_ptr; (void)cg; - switch (id) { + switch (id) { case SR_CONF_CONN: - if (!sdi || !sdi->conn) + if (!sdi || !sdi->conn) return SR_ERR_ARG; - usb = sdi->conn; - if (usb->address == 255) - /* Device still needs to re-enumerate after firmware - * upload, so we don't know its (future) address. */ + usb = sdi->conn; + if (usb->address == 255) + /* Device still needs to re-enumerate after firmware + * upload, so we don't know its (future) address. */ return SR_ERR; - snprintf(str, 128, "%d.%d", usb->bus, usb->address); + snprintf(str, 128, "%d.%d", usb->bus, usb->address); *data = g_variant_new_string(str); - break; + break; case SR_CONF_LIMIT_SAMPLES: if (!sdi) return SR_ERR; @@ -1157,11 +1128,11 @@ static int config_get(int id, GVariant **data, const struct sr_dev_inst *sdi, *data = g_variant_new_uint64(devc->limit_samples); break; case SR_CONF_SAMPLERATE: - if (!sdi) + if (!sdi) return SR_ERR; - devc = sdi->priv; - *data = g_variant_new_uint64(devc->cur_samplerate); - break; + devc = sdi->priv; + *data = g_variant_new_uint64(devc->cur_samplerate); + break; case SR_CONF_CLOCK_TYPE: if (!sdi) return SR_ERR; @@ -1270,6 +1241,12 @@ static int config_get(int id, GVariant **data, const struct sr_dev_inst *sdi, devc = sdi->priv; *data = g_variant_new_boolean(devc->zero); break; + case SR_CONF_CALI: + if (!sdi) + return SR_ERR; + devc = sdi->priv; + *data = g_variant_new_boolean(devc->cali); + break; case SR_CONF_STREAM: if (!sdi) return SR_ERR; @@ -1305,9 +1282,60 @@ static int config_get(int id, GVariant **data, const struct sr_dev_inst *sdi, return SR_ERR; *data = g_variant_new_uint64(DSCOPE_MAX_DEPTH); break; + case SR_CONF_VGAIN: + if (!sdi || !ch) + return SR_ERR; + *data = g_variant_new_uint64(dso_vga(sdi, ch)>>8); + break; + case SR_CONF_VGAIN_DEFAULT: + if (!sdi || !ch) + return SR_ERR; + vga_ptr = get_vga_ptr(sdi); + for (i = 0; vga_ptr && (vga_ptr+i)->key; i++) { + if ((vga_ptr+i)->key == ch->vdiv) + break; + } + *data = g_variant_new_uint64(get_default_vgain(sdi, i)>>8); + break; + case SR_CONF_VGAIN_RANGE: + if (!sdi) + return SR_ERR; + vga_ptr = get_vga_ptr(sdi); + for (i = 0; vga_ptr && (vga_ptr+i)->key; i++) { + if ((vga_ptr+i)->key == ch->vdiv) + break; + } + uint16_t vgain_default= (get_default_vgain(sdi, i)>>8) & 0x0FFF; + *data = g_variant_new_uint16(min(CALI_VGAIN_RANGE, vgain_default*2)); + break; + case SR_CONF_VOFF: + if (!sdi || !ch) + return SR_ERR; + uint16_t voff = dso_voff(sdi, ch); + uint16_t voff_default = get_default_voff(sdi, ch->index); + if (strcmp(sdi->model, "DSCope") == 0) { + int voff_skew_coarse = (voff >> 10) - (voff_default >> 10); + int voff_skew_fine = (voff & 0x03ff) - (voff_default & 0x03ff); + double trans_coarse = (ch->vdiv < 500) ? (ch->vpos_trans >> 8)/DSCOPE_TRANS_CMULTI : (ch->vpos_trans >> 8); + double trans_fine = (ch->vdiv < 500) ? (ch->vpos_trans & 0x00ff) / 1000.0 : (ch->vpos_trans & 0x00ff) / DSCOPE_TRANS_FMULTI; + double voff_rate = (voff_skew_coarse*trans_coarse - voff_skew_fine*trans_fine) / ch->vdiv; + voff = (voff_rate * 0.5 + 0.5) * CALI_VOFF_RANGE; + } + *data = g_variant_new_uint16(voff); + break; + case SR_CONF_VOFF_DEFAULT: + if (!sdi || !ch) + return SR_ERR; + *data = g_variant_new_uint16(get_default_voff(sdi, ch->index)); + break; + case SR_CONF_VOFF_RANGE: + if (!sdi) + return SR_ERR; + *data = g_variant_new_uint16(CALI_VOFF_RANGE); + break; default: return SR_ERR_NA; - } + } return SR_OK; } @@ -1326,11 +1354,11 @@ static int config_set(int id, GVariant *data, struct sr_dev_inst *sdi, if (sdi->status != SR_ST_ACTIVE) return SR_ERR; - devc = sdi->priv; + devc = sdi->priv; usb = sdi->conn; if (id == SR_CONF_SAMPLERATE) { - devc->cur_samplerate = g_variant_get_uint64(data); + devc->cur_samplerate = g_variant_get_uint64(data); if (sdi->mode == LOGIC) { if (devc->cur_samplerate >= SR_MHZ(200)) { adjust_probes(sdi, SR_MHZ(1600)/devc->cur_samplerate); @@ -1364,7 +1392,7 @@ static int config_set(int id, GVariant *data, struct sr_dev_inst *sdi, } ret = SR_OK; } else if (id == SR_CONF_LIMIT_SAMPLES) { - devc->limit_samples = g_variant_get_uint64(data); + devc->limit_samples = g_variant_get_uint64(data); ret = SR_OK; } else if (id == SR_CONF_DEVICE_MODE) { sdi->mode = g_variant_get_int16(data); @@ -1572,62 +1600,162 @@ static int config_set(int id, GVariant *data, struct sr_dev_inst *sdi, __func__, devc->trigger_holdoff); } else if (id == SR_CONF_ZERO) { devc->zero = g_variant_get_boolean(data); - } else if (id == SR_CONF_ZERO_SET) { + if (devc->zero) { + devc->zero_stage = -1; + devc->zero_pcnt = 0; + devc->zero_comb = -1; + GList *l; + int i; + struct DSL_vga *vga_ptr = get_vga_ptr(sdi); + for(l = sdi->channels; l; l = l->next) { + struct sr_channel *probe = (struct sr_channel *)l->data; + probe->vpos_trans = get_default_trans(sdi); + } + for (i = 0; vga_ptr && (vga_ptr+i)->key; i++) { + (vga_ptr+i)->vgain0 = get_default_vgain(sdi, i); + (vga_ptr+i)->vgain1 = get_default_vgain(sdi, i); + (vga_ptr+i)->voff0 = get_default_voff(sdi, 0); + (vga_ptr+i)->voff1 = get_default_voff(sdi, 1); + } + } + } else if (id == SR_CONF_CALI) { + devc->cali = g_variant_get_boolean(data); + } else if (id == SR_CONF_ZERO_LOAD) { GSList *l; for(l = sdi->channels; l; l = l->next) { struct sr_channel *probe = (struct sr_channel *)l->data; - zero_info.zero_addr = zero_base_addr + probe->index * sizeof(struct cmd_zero_info); - zero_info.vpos_l = (probe->index == 0) ? mstatus.ch0_vpos_mid : mstatus.ch1_vpos_mid; - zero_info.vpos_h = (probe->index == 0) ? mstatus.ch0_vpos_mid >> 8 : mstatus.ch1_vpos_mid >> 8; - zero_info.voff_l = (probe->index == 0) ? mstatus.ch0_voff_mid : mstatus.ch1_voff_mid; - zero_info.voff_h = (probe->index == 0) ? mstatus.ch0_voff_mid >> 8 : mstatus.ch1_voff_mid >> 8; - zero_info.vcntr_l = (probe->index == 0) ? mstatus.ch0_vcntr : mstatus.ch1_vcntr; - zero_info.vcntr_h = (probe->index == 0) ? mstatus.ch0_vcntr >> 8 : mstatus.ch1_vcntr >> 8; - zero_info.adc_off = (probe->index == 0) ? mstatus.ch0_adc_off + (mstatus.ch0_adc_sign << 7) : mstatus.ch1_adc_off + (mstatus.ch1_adc_sign << 7); - ret = command_wr_nvm(usb->devhdl, (unsigned char *)&zero_info, sizeof(struct cmd_zero_info)); - if (ret != SR_OK) - sr_err("DSO channel %d Set Zero command failed!", probe->index); + if (!dso_load_eep(sdi, probe)) { + config_set(SR_CONF_ZERO, g_variant_new_boolean(TRUE), sdi, NULL, NULL); + sr_info("Zero have not been setted!"); + break; + } } - comb_info.comb_addr = comb_base_addr; - comb_info.comb0_low_off = mstatus.comb0_off; - comb_info.comb0_hig_off = mstatus.comb0_off >> 8; - comb_info.comb1_low_off = mstatus.comb1_off; - comb_info.comb1_hig_off = mstatus.comb1_off >> 8; - comb_info.comb_sign = mstatus.comb_sign; - ret = command_wr_nvm(usb->devhdl, (unsigned char *)&comb_info, sizeof(struct cmd_comb_info)); - if (ret != SR_OK) - sr_err("DSO Set Comb command failed!"); - else - devc->zero = FALSE; - } else { - ret = SR_ERR_NA; - } + } else if (id == SR_CONF_ZERO_SET) { + GSList *l; + struct cmd_zero_info zero_info; + struct cmd_vga_info vga_info; + for(l = sdi->channels; l; l = l->next) { + struct sr_channel *probe = (struct sr_channel *)l->data; + zero_info.zero_addr = zero_base_addr + + probe->index * (sizeof(struct cmd_zero_info) + sizeof(struct cmd_vga_info)); + int i; + struct DSL_vga *vga_ptr = get_vga_ptr(sdi); + uint8_t *voff_ptr = &zero_info.zero_addr + 1; + for (i = 0; vga_ptr && (vga_ptr+i)->key; i++) { + *(voff_ptr+2*i) = ((probe->index == 0) ? (vga_ptr+i)->voff0 : (vga_ptr+i)->voff1) & 0x00ff; + *(voff_ptr+2*i+1) = ((probe->index == 0) ? (vga_ptr+i)->voff0 : (vga_ptr+i)->voff1) >> 8; + } + if (i != 0) { + *(voff_ptr+2*i) = probe->comb_diff_top; + *(voff_ptr+2*i+1) = probe->comb_diff_bom; + *(voff_ptr+2*i+2) = (probe->vpos_trans&0x00FF); + *(voff_ptr+2*i+3) = (probe->vpos_trans>>8); - return ret; + vga_info.vga_addr = zero_info.zero_addr + sizeof(struct cmd_zero_info); + uint16_t *vgain_ptr = &vga_info.vga0; + for (i=0; vga_ptr && (vga_ptr+i)->key; i++){ + *(vgain_ptr+i) = ((probe->index == 0) ? (vga_ptr+i)->vgain0 : (vga_ptr+i)->vgain1) >> 8; + } + ret = command_wr_reg(usb->devhdl, 0, EEWP_ADDR); + if (ret == SR_OK) + ret = command_wr_nvm(usb->devhdl, (unsigned char *)&zero_info, sizeof(struct cmd_zero_info)); + if (ret == SR_OK) + ret = command_wr_nvm(usb->devhdl, (unsigned char *)&vga_info, sizeof(struct cmd_vga_info)); + if (ret == SR_OK) + ret = command_wr_reg(usb->devhdl, 1, EEWP_ADDR); + if (ret != SR_OK) + sr_err("DSO channel %d Set Zero command failed!", probe->index); + + const double slope = (probe->comb_diff_bom - probe->comb_diff_top)/(2.0*255.0); + for (i = 0; i < 256; i++) { + ret = command_wr_reg(usb->devhdl, i, COMB_ADDR + probe->index*2); + int value = i+i*slope+probe->comb_diff_top*0.5+0.5; + value = (value < 0) ? 0 : + (value > 255) ? 255 : value; + ret = command_wr_reg(usb->devhdl, value, COMB_ADDR + probe->index*2 + 1); + } + } + } + } else if (id == SR_CONF_VOCM) { + const uint8_t vocm = g_variant_get_byte(data); + ret = command_wr_reg(usb->devhdl, vocm, COMB_ADDR+4); + } else if (id == SR_CONF_VGAIN) { + const uint64_t vgain = g_variant_get_uint64(data) << 8; + int i; + struct DSL_vga *vga_ptr = get_vga_ptr(sdi); + for (i = 0; vga_ptr && (vga_ptr+i)->key; i++) { + if ((vga_ptr+i)->key == ch->vdiv) + if (ch->index == 0) + (vga_ptr+i)->vgain0 = vgain; + else if (ch->index == 1) + (vga_ptr+i)->vgain1 = vgain; + } + ret = command_dso_ctrl(usb->devhdl, dso_cmd_gen(sdi, ch, SR_CONF_VDIV)); + if (ret == SR_OK) + sr_dbg("%s: setting VDIV of channel %d to %d mv", + __func__, ch->index, ch->vdiv); + else + sr_dbg("%s: setting VDIV of channel %d to %d mv failed", + __func__, ch->index, ch->vdiv); + } else if (id == SR_CONF_VOFF) { + uint16_t voff = g_variant_get_uint16(data); + if (strcmp(sdi->model, "DSCope") == 0) { + double voltage_off = (2.0 * voff / CALI_VOFF_RANGE - 1) * ch->vdiv; + double trans_coarse = (ch->vdiv < 500) ? (ch->vpos_trans >> 8)/DSCOPE_TRANS_CMULTI : (ch->vpos_trans >> 8); + double trans_fine = (ch->vdiv < 500) ? (ch->vpos_trans & 0x00ff) / 1000.0 : (ch->vpos_trans & 0x00ff) / DSCOPE_TRANS_FMULTI; + + uint16_t default_voff = get_default_voff(sdi, ch->index); + int voff_coarse = floor(voltage_off / trans_coarse + 0.5); + int voff_fine = floor(-(voltage_off - voff_coarse*trans_coarse)/trans_fine + 0.5); + voff_coarse = (default_voff >> 10) + voff_coarse; + voff_fine = (default_voff&0x03ff) + voff_fine; + voff = (voff_coarse << 10) + voff_fine; + } + int i; + struct DSL_vga *vga_ptr = get_vga_ptr(sdi); + for (i = 0; vga_ptr && (vga_ptr+i)->key; i++) { + if ((vga_ptr+i)->key == ch->vdiv) + if (ch->index == 0) + (vga_ptr+i)->voff0 = voff; + else if (ch->index == 1) + (vga_ptr+i)->voff1 = voff; + } + ret = command_dso_ctrl(usb->devhdl, dso_cmd_gen(sdi, ch, SR_CONF_VPOS)); + if (ret == SR_OK) + sr_dbg("%s: setting VPOS of channel %d to %lf mv", + __func__, ch->index, ch->vpos); + else + sr_dbg("%s: setting VPOS of channel %d to %lf mv failed", + __func__, ch->index, ch->vpos); + }else { + ret = SR_ERR_NA; + } + + return ret; } static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi, const struct sr_channel_group *cg) { - GVariant *gvar; - GVariantBuilder gvb; + GVariant *gvar; + GVariantBuilder gvb; - (void)sdi; + (void)sdi; (void)cg; - switch (key) { + switch (key) { case SR_CONF_SCAN_OPTIONS: // *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32, // hwopts, ARRAY_SIZE(hwopts), sizeof(int32_t)); *data = g_variant_new_from_data(G_VARIANT_TYPE("ai"), hwopts, ARRAY_SIZE(hwopts)*sizeof(int32_t), TRUE, NULL, NULL); - break; + break; case SR_CONF_DEVICE_OPTIONS: // *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32, // hwcaps, ARRAY_SIZE(hwcaps), sizeof(int32_t)); *data = g_variant_new_from_data(G_VARIANT_TYPE("ai"), hwcaps, ARRAY_SIZE(hwcaps)*sizeof(int32_t), TRUE, NULL, NULL); - break; + break; case SR_CONF_DEVICE_CONFIGS: // *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32, // hwcaps, ARRAY_SIZE(hwcaps), sizeof(int32_t)); @@ -1639,14 +1767,14 @@ static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi, sessions, ARRAY_SIZE(sessions)*sizeof(int32_t), TRUE, NULL, NULL); break; case SR_CONF_SAMPLERATE: - g_variant_builder_init(&gvb, G_VARIANT_TYPE("a{sv}")); + g_variant_builder_init(&gvb, G_VARIANT_TYPE("a{sv}")); // gvar = g_variant_new_fixed_array(G_VARIANT_TYPE("t"), samplerates, // ARRAY_SIZE(samplerates), sizeof(uint64_t)); gvar = g_variant_new_from_data(G_VARIANT_TYPE("at"), samplerates, ARRAY_SIZE(samplerates)*sizeof(uint64_t), TRUE, NULL, NULL); g_variant_builder_add(&gvb, "{sv}", "samplerates", gvar); - *data = g_variant_builder_end(&gvb); - break; + *data = g_variant_builder_end(&gvb); + break; case SR_CONF_LIMIT_SAMPLES: g_variant_builder_init(&gvb, G_VARIANT_TYPE("a{sv}")); gvar = g_variant_new_from_data(G_VARIANT_TYPE("at"), @@ -1656,7 +1784,7 @@ static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi, break; case SR_CONF_TRIGGER_TYPE: *data = g_variant_new_string(TRIGGER_TYPE); - break; + break; case SR_CONF_OPERATION_MODE: *data = g_variant_new_strv(opmodes, opmodes_show_count); break; @@ -1666,13 +1794,364 @@ static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi, case SR_CONF_FILTER: *data = g_variant_new_strv(filters, ARRAY_SIZE(filters)); break; - default: + default: return SR_ERR_NA; - } + } return SR_OK; } +static int dso_init(struct sr_dev_inst *sdi, gboolean from_eep) +{ + int ret, i; + GSList *l; + struct sr_usb_dev_inst *usb = sdi->conn; + gboolean zeroed = TRUE; + + for(l = sdi->channels; l; l = l->next) { + struct sr_channel *probe = (struct sr_channel *)l->data; + ret = command_dso_ctrl(usb->devhdl, dso_cmd_gen(sdi, probe, SR_CONF_COUPLING)); + if (ret != SR_OK) { + sr_err("DSO set coupling of channel %d command failed!", probe->index); + return ret; + } + ret = command_dso_ctrl(usb->devhdl, dso_cmd_gen(sdi, probe, SR_CONF_VDIV)); + if (ret != SR_OK) { + sr_err("Set VDIV of channel %d command failed!", probe->index); + return ret; + } + if (from_eep && zeroed) { + zeroed = dso_load_eep(sdi, probe); + } + ret = command_dso_ctrl(usb->devhdl, dso_cmd_gen(sdi, probe, SR_CONF_VPOS)); + if (ret != SR_OK) { + sr_err("Set VPOS of channel %d command failed!", probe->index); + return ret; + } + } + if (!zeroed) { + for(l = sdi->channels; l; l = l->next) { + struct sr_channel *probe = (struct sr_channel *)l->data; + for (i = 0; i < 256; i++) { + ret = command_wr_reg(usb->devhdl, i, COMB_ADDR + probe->index*2); + ret = command_wr_reg(usb->devhdl, i, COMB_ADDR + probe->index*2 + 1); + } + } + config_set(SR_CONF_ZERO, g_variant_new_boolean(TRUE), sdi, NULL, NULL); + sr_info("Zero have not been setted!"); + } + + ret = command_dso_ctrl(usb->devhdl, dso_cmd_gen(sdi, 0, SR_CONF_SAMPLERATE)); + if (ret != SR_OK) { + sr_err("Set Sample Rate command failed!"); + return ret; + } + ret = command_dso_ctrl(usb->devhdl, dso_cmd_gen(sdi, NULL, SR_CONF_HORIZ_TRIGGERPOS)); + if (ret != SR_OK) { + sr_err("Set Horiz Trigger Position command failed!"); + return ret; + } + ret = command_dso_ctrl(usb->devhdl, dso_cmd_gen(sdi, NULL, SR_CONF_TRIGGER_HOLDOFF)); + if (ret != SR_OK) { + sr_err("Set Trigger Holdoff Time command failed!"); + return ret; + } + ret = command_dso_ctrl(usb->devhdl, dso_cmd_gen(sdi, NULL, SR_CONF_TRIGGER_SLOPE)); + if (ret != SR_OK) { + sr_err("Set Trigger Slope command failed!"); + return ret; + } + ret = command_dso_ctrl(usb->devhdl, dso_cmd_gen(sdi, NULL, SR_CONF_TRIGGER_SOURCE)); + if (ret != SR_OK) { + sr_err("Set Trigger Source command failed!"); + return ret; + } + ret = command_dso_ctrl(usb->devhdl, dso_cmd_gen(sdi, NULL, SR_CONF_TRIGGER_VALUE)); + if (ret != SR_OK) { + sr_err("Set Trigger Value command failed!"); + return ret; + } + return ret; +} + +static int dso_zero(struct sr_dev_inst *sdi, struct sr_status mstatus) +{ + struct DSL_context *devc = sdi->priv; + struct sr_usb_dev_inst *usb = sdi->conn; + GSList *l; + int ret, i; + static double vpos_back[2]; + static uint64_t vdiv_back[2]; + struct DSL_vga *vga_ptr = get_vga_ptr(sdi); + struct sr_channel *probe0, *probe1; + for(l = sdi->channels; l; l = l->next) { + struct sr_channel *probe = (struct sr_channel *)l->data; + if (probe->index == 0) + probe0 = probe; + if (probe->index == 1) + probe1 = probe; + } + + if (devc->zero_stage == -1) { + // initialize before zero adjustment + if (dso_init(sdi, 0) == SR_OK) + devc->zero_stage = 0; + } else if ((vga_ptr+devc->zero_stage)->key == 0) { + ret = SR_OK; + if (strcmp(sdi->model, "DSCope20") == 0) { + if (devc->zero_pcnt == 0) { + devc->zero_comb = 0; + vpos_back[0] = probe0->vpos; + probe0->vpos = (vga_ptr+devc->zero_stage-1)->key * -4.8; + vdiv_back[0] = probe0->vdiv; + probe0->vdiv = (vga_ptr+devc->zero_stage-1)->key; + ret = command_dso_ctrl(usb->devhdl, dso_cmd_gen(sdi, probe0, SR_CONF_VPOS)); + } else if (devc->zero_pcnt == 4) { + const double voff = 255*0.98 - (mstatus.ch0_max + mstatus.ch0_min) / 2.0; + if (abs(voff) < 0.5) { + probe0->vpos = vpos_back[0]; + } else { + probe0->vpos_trans += voff; + ret = command_dso_ctrl(usb->devhdl, dso_cmd_gen(sdi, probe0, SR_CONF_VPOS)); + devc->zero_pcnt = 1; + } + } else if (devc->zero_pcnt == 5) { + devc->zero_comb = 0; + vpos_back[1] = probe1->vpos; + probe1->vpos = (vga_ptr+devc->zero_stage-1)->key * -4.8; + vdiv_back[1] = probe1->vdiv; + probe1->vdiv = (vga_ptr+devc->zero_stage-1)->key; + ret = command_dso_ctrl(usb->devhdl, dso_cmd_gen(sdi, probe1, SR_CONF_VPOS)); + } else if (devc->zero_pcnt == 9) { + const double voff = 255*0.98 - (mstatus.ch1_max + mstatus.ch1_min) / 2.0; + if (abs(voff) < 0.5) { + probe1->vpos = vpos_back[1]; + } else { + probe1->vpos_trans += voff; + ret = command_dso_ctrl(usb->devhdl, dso_cmd_gen(sdi, probe1, SR_CONF_VPOS)); + devc->zero_pcnt = 6; + } + } + } + + if (devc->zero_pcnt == 10) { + ret = command_wr_reg(usb->devhdl, 0b1101, COMB_ADDR+6); + devc->zero_comb = 0; + vpos_back[0] = probe0->vpos; + probe0->vpos = (vga_ptr+devc->zero_stage-1)->key * 4.5; + ret = command_dso_ctrl(usb->devhdl, dso_cmd_gen(sdi, probe0, SR_CONF_VPOS)); + } else if (devc->zero_pcnt == 15) { + probe0->comb_diff_top = (mstatus.ch0_max - mstatus.ch1_max) + + (mstatus.ch0_min - mstatus.ch1_min); + probe0->vpos = (vga_ptr+devc->zero_stage-1)->key * -4.5; + ret = command_dso_ctrl(usb->devhdl, dso_cmd_gen(sdi, probe0, SR_CONF_VPOS)); + } else if (devc->zero_pcnt == 20) { + probe0->comb_diff_bom = (mstatus.ch0_max - mstatus.ch1_max) + + (mstatus.ch0_min - mstatus.ch1_min); + probe0->vpos = vpos_back[0]; + ret = command_dso_ctrl(usb->devhdl, dso_cmd_gen(sdi, probe0, SR_CONF_VPOS)); + } + + if (devc->zero_pcnt == 25) { + ret = command_wr_reg(usb->devhdl, 0b1110, COMB_ADDR+6); + devc->zero_comb = 1; + vpos_back[1] = probe1->vpos; + probe1->vpos = (vga_ptr+devc->zero_stage-1)->key * 4.5; + ret = command_dso_ctrl(usb->devhdl, dso_cmd_gen(sdi, probe1, SR_CONF_VPOS)); + } else if (devc->zero_pcnt == 30) { + probe1->comb_diff_top = (mstatus.ch1_max - mstatus.ch0_max) + + (mstatus.ch1_min - mstatus.ch0_min); + probe1->vpos = (vga_ptr+devc->zero_stage-1)->key * -4.5; + ret = command_dso_ctrl(usb->devhdl, dso_cmd_gen(sdi, probe1, SR_CONF_VPOS)); + } else if (devc->zero_pcnt == 35) { + probe1->comb_diff_bom = (mstatus.ch1_max - mstatus.ch0_max) + + (mstatus.ch1_min - mstatus.ch0_min); + probe1->vpos = vpos_back[1]; + ret = command_dso_ctrl(usb->devhdl, dso_cmd_gen(sdi, probe1, SR_CONF_VPOS)); + } + + if (devc->zero_pcnt == 40) { + if (strcmp(sdi->model, "DSCope20") == 0) { + probe0->vdiv = vdiv_back[0]; + probe1->vdiv = vdiv_back[1]; + } + ret = command_wr_reg(usb->devhdl, 0b0011, COMB_ADDR+6); + devc->zero = FALSE; + dso_init(sdi, 0); + } + + if (ret == SR_OK) + devc->zero_pcnt++; + } else { + if (devc->zero_pcnt == 0) { + for(l = sdi->channels; l; l = l->next) { + struct sr_channel *probe = (struct sr_channel *)l->data; + uint64_t vdiv_back = probe->vdiv; + probe->vdiv = (vga_ptr+devc->zero_stage)->key; + ret = command_dso_ctrl(usb->devhdl, dso_cmd_gen(sdi, probe, SR_CONF_VDIV)); + ret = command_dso_ctrl(usb->devhdl, dso_cmd_gen(sdi, probe, SR_CONF_VPOS)); + probe->vdiv = vdiv_back; + } + } + + if (devc->zero_pcnt == 4) { + const double voff0 = 255/2.0 - (mstatus.ch0_max + mstatus.ch0_min)/2.0; + const double voff1 = 255/2.0 - (mstatus.ch1_max + mstatus.ch1_min)/2.0; + if (abs(voff0) < 0.5 && abs(voff1) < 0.5) { + devc->zero_stage++; + } else { + if (strcmp(sdi->model, "DSCope") == 0) { + for(l = sdi->channels; l; l = l->next) { + struct sr_channel *probe = (struct sr_channel *)l->data; + double trans_coarse = ((vga_ptr+devc->zero_stage)->key < 500) ? (probe->vpos_trans >> 8)/DSCOPE_TRANS_CMULTI : (probe->vpos_trans >> 8); + double trans_fine = ((vga_ptr+devc->zero_stage)->key < 500) ? (probe->vpos_trans & 0x00ff) / 1000.0 : (probe->vpos_trans & 0x00ff) / DSCOPE_TRANS_FMULTI; + + double voltage_off = ((probe->index == 0) ? voff0 : voff1) * (vga_ptr+devc->zero_stage)->key * 10 / 255.0; + uint16_t last_voff = ((probe->index == 0) ? (vga_ptr+devc->zero_stage)->voff0 : (vga_ptr+devc->zero_stage)->voff1); + int voff_coarse = floor(voltage_off / trans_coarse + 0.5); + int voff_fine = floor(-(voltage_off - voff_coarse*trans_coarse)/trans_fine + 0.5); + voff_coarse = (last_voff >> 10) + voff_coarse; + voff_fine = (last_voff&0x03ff) + voff_fine; + if (probe->index == 0) + (vga_ptr+devc->zero_stage)->voff0 = (voff_coarse << 10) + voff_fine; + else if (probe->index == 1) + (vga_ptr+devc->zero_stage)->voff1 = (voff_coarse << 10) + voff_fine; + } + } else if (strcmp(sdi->model, "DSCope20") == 0) { + (vga_ptr+devc->zero_stage)->voff0 += voff0; + (vga_ptr+devc->zero_stage)->voff1 += voff1; + } + } + devc->zero_pcnt = 0; + } else { + devc->zero_pcnt++; + } + } + + return ret; +} + +static int dev_open(struct sr_dev_inst *sdi) +{ + struct sr_usb_dev_inst *usb; + struct DSL_context *devc; + int ret; + int64_t timediff_us, timediff_ms; + int i; + + devc = sdi->priv; + usb = sdi->conn; + + /* + * If the firmware was recently uploaded, wait up to MAX_RENUM_DELAY_MS + * milliseconds for the FX2 to renumerate. + */ + ret = SR_ERR; + if (devc->fw_updated > 0) { + sr_info("Waiting for device to reset."); + /* Takes >= 300ms for the FX2 to be gone from the USB bus. */ + g_usleep(300 * 1000); + timediff_ms = 0; + while (timediff_ms < MAX_RENUM_DELAY_MS) { + if ((ret = DSCope_dev_open(sdi)) == SR_OK) + break; + g_usleep(100 * 1000); + + timediff_us = g_get_monotonic_time() - devc->fw_updated; + timediff_ms = timediff_us / 1000; + sr_spew("Waited %" PRIi64 "ms.", timediff_ms); + } + if (ret != SR_OK) { + sr_err("Device failed to renumerate."); + return SR_ERR; + } + sr_info("Device came back after %" PRIi64 "ms.", timediff_ms); + } else { + sr_info("Firmware upload was not needed."); + ret = DSCope_dev_open(sdi); + } + + if (ret != SR_OK) { + sr_err("Unable to open device."); + return SR_ERR; + } + + ret = libusb_claim_interface(usb->devhdl, USB_INTERFACE); + if (ret != 0) { + switch(ret) { + case LIBUSB_ERROR_BUSY: + sr_err("Unable to claim USB interface. Another " + "program or driver has already claimed it."); + break; + case LIBUSB_ERROR_NO_DEVICE: + sr_err("Device has been disconnected."); + break; + default: + sr_err("Unable to claim interface: %s.", + libusb_error_name(ret)); + break; + } + + return SR_ERR; + } + + if ((ret = command_fpga_config(usb->devhdl)) != SR_OK) { + sr_err("Send FPGA configure command failed!"); + } else { + /* Takes >= 10ms for the FX2 to be ready for FPGA configure. */ + g_usleep(10 * 1000); + char *fpga_bit = malloc(strlen(config_path)+strlen(devc->profile->fpga_bit33)+1); + if (fpga_bit == NULL) + return SR_ERR_MALLOC; + strcpy(fpga_bit, config_path); + strcat(fpga_bit, devc->profile->fpga_bit33); + ret = fpga_config(usb->devhdl, fpga_bit); + if (ret != SR_OK) { + sr_err("Configure FPGA failed!"); + } + } + + if (sdi->mode == DSO) + dso_init(sdi, 1); + + return SR_OK; +} + +static int dev_close(struct sr_dev_inst *sdi) +{ + struct sr_usb_dev_inst *usb; + + usb = sdi->conn; + if (usb->devhdl == NULL) + return SR_ERR; + + sr_info("DSCope: Closing device %d on %d.%d interface %d.", + sdi->index, usb->bus, usb->address, USB_INTERFACE); + libusb_release_interface(usb->devhdl, USB_INTERFACE); + libusb_close(usb->devhdl); + usb->devhdl = NULL; + sdi->status = SR_ST_INACTIVE; + + return SR_OK; +} + +static int cleanup(void) +{ + int ret; + struct drv_context *drvc; + + if (!(drvc = di->priv)) + return SR_OK; + + ret = dev_clear(); + + g_free(drvc); + di->priv = NULL; + + return ret; +} + + static void abort_acquisition(struct DSL_context *devc) { int i; @@ -1778,7 +2257,7 @@ static struct sr_config * new_config(int key, GVariant *data) static void receive_transfer(struct libusb_transfer *transfer) { - gboolean packet_has_error = FALSE; + gboolean packet_has_error = FALSE; struct sr_datafeed_packet packet; struct sr_datafeed_logic logic; struct sr_datafeed_dso dso; @@ -1920,6 +2399,7 @@ static void receive_transfer(struct libusb_transfer *transfer) uint16_t channel_cnt = 0; uint16_t channel_en_cnt = 0; GSList *l; + int ret; struct sr_dev_inst *sdi = devc->cb_data; for (l = sdi->channels; l; l = l->next) { struct sr_channel *probe = (struct sr_channel *)l->data; @@ -1945,24 +2425,14 @@ static void receive_transfer(struct libusb_transfer *transfer) mstatus.stream_mode = *((const uint32_t*)cur_buf + mstatus_offset/2 + 16/2) & 0x80000000; mstatus.sample_divider = *((const uint32_t*)cur_buf + mstatus_offset/2 + 18/2) & 0x7fffffff; mstatus.sample_divider_tog = *((const uint32_t*)cur_buf + mstatus_offset/2 + 18/2) & 0x80000000; - mstatus.zeroing = (*((const uint16_t*)cur_buf + mstatus_offset + 128) & 0x8000) != 0; - mstatus.ch0_vpos_mid = *((const uint16_t*)cur_buf + mstatus_offset + 128) & 0x7fff; - mstatus.ch0_voff_mid = *((const uint16_t*)cur_buf + mstatus_offset + 129); - mstatus.ch0_vcntr = *((const uint16_t*)cur_buf + mstatus_offset + 130); - mstatus.ch0_adc_off = *((const uint8_t*)cur_buf + mstatus_offset*2 + 131*2); - mstatus.ch0_adc_sign = *((const uint8_t*)cur_buf + mstatus_offset*2 + 131*2+1); - mstatus.ch1_vpos_mid = *((const uint16_t*)cur_buf + mstatus_offset + 132); - mstatus.ch1_voff_mid = *((const uint16_t*)cur_buf + mstatus_offset + 133); - mstatus.ch1_vcntr = *((const uint16_t*)cur_buf + mstatus_offset + 134); - mstatus.ch1_adc_off = *((const uint8_t*)cur_buf + mstatus_offset*2 + 135*2); - mstatus.ch1_adc_sign = *((const uint8_t*)cur_buf + mstatus_offset*2 + 135*2+1); - mstatus.comb0_off = *((const uint16_t*)cur_buf + mstatus_offset + 136); - mstatus.comb1_off = *((const uint16_t*)cur_buf + mstatus_offset + 137); - mstatus.comb_sign = *((const uint8_t*)cur_buf + mstatus_offset*2 + 138*2); } else { mstatus.vlen = instant_buffer_size; } + if (devc->zero) { + dso_zero(sdi, mstatus); + } + const uint32_t divider = devc->zero ? 0x1 : (uint32_t)ceil(DSCOPE_MAX_SAMPLERATE * 1.0 / devc->cur_samplerate / channel_en_cnt); if ((mstatus.sample_divider == divider && mstatus.vlen != 0 && @@ -2394,9 +2864,14 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data) devc->transfers[0] = transfer; devc->submitted_transfers++; + if (devc->zero && devc->zero_stage == -1) { + // initialize before zero adjustment + if (dso_init(sdi, 0) == SR_OK) + devc->zero_stage = 0; + } + devc->status = DSL_START; mstatus_valid = FALSE; - mstatus.zeroing = devc->zero; /* Send header packet to the session bus. */ //std_session_send_df_header(cb_data, LOG_PREFIX); std_session_send_df_header(sdi, LOG_PREFIX); @@ -2447,10 +2922,7 @@ static int dev_status_get(struct sr_dev_inst *sdi, struct sr_status *status, int devc = sdi->priv; if (mstatus_valid) { *status = mstatus; - if (devc->zero) - return SR_ERR; - else - return SR_OK; + return SR_OK; } else { return SR_ERR; } diff --git a/libsigrok4DSL/hardware/DSL/dsl.h b/libsigrok4DSL/hardware/DSL/dsl.h index 2a693e70..1b3f3c4e 100644 --- a/libsigrok4DSL/hardware/DSL/dsl.h +++ b/libsigrok4DSL/hardware/DSL/dsl.h @@ -2,7 +2,7 @@ * This file is part of the libsigrok project. * * Copyright (C) 2013 Bert Vermeulen - * Copyright (C) 2013 DreamSourceLab + * Copyright (C) 2013 DreamSourceLab * * 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 @@ -78,6 +78,49 @@ #define DSCOPE_MAX_SAMPLERATE SR_MHZ(200) #define DSCOPE_INSTANT_DEPTH SR_MB(32) +/* + * for DSCope device + * trans: x << 8 + y + * x = vpos(coarse), each step(1024 total) indicate x(mv) at 1/20 attenuation, and x/10(mv) at 1/2 attenuation + * y = voff(fine), each step(1024 total) indicate y/100(mv) at 1/20 attenuation, adn y/1000(mv) at 1/2 attenuation + * voff: x << 10 + y + * x = vpos(coarse) default bias + * y = voff(fine) default bias + * the final offset: x+DSCOPE_CONSTANT_BIAS->vpos(coarse); y->voff(fine) + */ +#define DSCOPE_DEFAULT_TRANS (129<<8)+167 +#define DSCOPE_DEFAULT_VOFF (32<<10)+558 +#define DSCOPE_CONSTANT_BIAS 160 +#define DSCOPE_TRANS_CMULTI 10 +#define DSCOPE_TRANS_FMULTI 100.0 +#define DSCOPE_DEFAULT_VGAIN0 0x162400 +#define DSCOPE_DEFAULT_VGAIN1 0x14C000 +#define DSCOPE_DEFAULT_VGAIN2 0x12E800 +#define DSCOPE_DEFAULT_VGAIN3 0x118000 +#define DSCOPE_DEFAULT_VGAIN4 0x102400 +#define DSCOPE_DEFAULT_VGAIN5 0x2E800 +#define DSCOPE_DEFAULT_VGAIN6 0x18000 +#define DSCOPE_DEFAULT_VGAIN7 0x02400 + +/* + * for DSCope20 device + * trans: the whole windows offset map to the offset pwm(1024 total) + * voff: offset pwm constant bias to balance circuit offset + */ +#define DSCOPE20_DEFAULT_TRANS 920 +#define DSCOPE20_DEFAULT_VOFF 45 +#define DSCOPE20_DEFAULT_VGAIN0 0x1DA800 +#define DSCOPE20_DEFAULT_VGAIN1 0x1A7200 +#define DSCOPE20_DEFAULT_VGAIN2 0x164200 +#define DSCOPE20_DEFAULT_VGAIN3 0x131800 +#define DSCOPE20_DEFAULT_VGAIN4 0xBD000 +#define DSCOPE20_DEFAULT_VGAIN5 0x7AD00 +#define DSCOPE20_DEFAULT_VGAIN6 0x48800 +#define DSCOPE20_DEFAULT_VGAIN7 0x12000 + +#define CALI_VGAIN_RANGE 100 +#define CALI_VOFF_RANGE (1024-DSCOPE20_DEFAULT_TRANS) + struct DSL_profile { uint16_t vid; uint16_t pid; @@ -113,7 +156,7 @@ static const struct DSL_profile supported_DSLogic[3] = { { 0, 0, 0, 0, 0, 0, 0, 0, 0 } }; -static const struct DSL_profile supported_DSCope[2] = { +static const struct DSL_profile supported_DSCope[3] = { /* * DSCope */ @@ -123,6 +166,12 @@ static const struct DSL_profile supported_DSCope[2] = { "DSCope.bin", DEV_CAPS_16BIT}, + {0x2A0E, 0x0004, NULL, "DSCope20", NULL, + "DSCope20.fw", + "DSCope20.bin", + "DSCope20.bin", + DEV_CAPS_16BIT}, + { 0, 0, 0, 0, 0, 0, 0, 0, 0 } }; @@ -176,6 +225,10 @@ struct DSL_context { uint32_t trigger_hpos; uint32_t trigger_holdoff; gboolean zero; + gboolean cali; + int zero_stage; + int zero_pcnt; + int zero_comb; gboolean stream; gboolean data_lock; @@ -254,4 +307,12 @@ struct DSL_setting { uint32_t end_sync; }; +struct DSL_vga { + int key; + uint64_t vgain0; + uint64_t vgain1; + uint16_t voff0; + uint16_t voff1; +}; + #endif diff --git a/libsigrok4DSL/hardware/DSL/dslogic.c b/libsigrok4DSL/hardware/DSL/dslogic.c index bed14429..b08feda5 100644 --- a/libsigrok4DSL/hardware/DSL/dslogic.c +++ b/libsigrok4DSL/hardware/DSL/dslogic.c @@ -245,7 +245,6 @@ extern struct ds_trigger *trigger; struct sr_status mstatus; struct cmd_zero_info zero_info; -struct cmd_comb_info comb_info; /** * Check the USB configuration to determine if this is an DSLogic device. @@ -279,7 +278,7 @@ static gboolean check_conf_profile(libusb_device *dev) if (libusb_get_string_descriptor_ascii(hdl, des.iProduct, strdesc, sizeof(strdesc)) < 0) break; - if (strncmp((const char *)strdesc, "DSLogic", 7)) + if (strncmp((const char *)strdesc, "USB-based Instrument", 20)) break; /* If we made it here, it must be an DSLogic. */ @@ -1000,28 +999,6 @@ static uint64_t dso_cmd_gen(struct sr_dev_inst *sdi, struct sr_channel* ch, int cmd += probe->trig_value << (8 * (probe->index + 1)); } break; - case SR_CONF_ZERO_SET: - cmd += 0x40; - cmd += ch->index << ch_bit; - cmd += ((uint64_t)zero_info.vpos_l << 8); - cmd += ((uint64_t)(zero_info.vpos_h & 0x3) << 16); - cmd += ((uint64_t)zero_info.voff_l << 24); - cmd += ((uint64_t)(zero_info.voff_h & 0x3) << 32); - cmd += ((uint64_t)zero_info.vcntr_l << 40); - cmd += ((uint64_t)(zero_info.vcntr_h & 0x3) << 48); - cmd += ((uint64_t)zero_info.adc_off << 56); - break; - case SR_CONF_COMB_SET: - cmd += 0x48; - cmd += ((uint64_t)comb_info.comb0_low_off << 8); - cmd += ((uint64_t)comb_info.comb0_hig_off << 16); - cmd += ((uint64_t)comb_info.comb1_low_off << 24); - cmd += ((uint64_t)comb_info.comb1_hig_off << 32); - cmd += ((uint64_t)comb_info.comb_sign << 40); - break; - case SR_CONF_ZERO_OVER: - cmd += 0x50; - break; case SR_CONF_TRIGGER_HOLDOFF: cmd += 0x58; cmd += devc->trigger_holdoff << 8; @@ -1128,7 +1105,7 @@ static int dev_open(struct sr_dev_inst *sdi) g_free(fpga_bit); } - ret = command_vth(usb->devhdl, devc->vth); + ret = command_wr_reg(usb->devhdl, (uint8_t)(devc->vth/5.0*255), VTH_ADDR); if (ret == SR_OK) sr_dbg("%s: setting threshold voltage to %d", __func__, devc->vth); @@ -1401,12 +1378,6 @@ static int config_get(int id, GVariant **data, const struct sr_dev_inst *sdi, devc = sdi->priv; *data = g_variant_new_uint64(DSLOGIC_MAX_LOGIC_DEPTH*ceil(samplerates[devc->samplerates_size-1] * 1.0 / DSLOGIC_MAX_LOGIC_SAMPLERATE)); break; - case SR_CONF_STATUS: - if (!sdi) - return SR_ERR; - devc = sdi->priv; - *data = g_variant_new_boolean(devc->status != DSL_INIT); - break; default: return SR_ERR_NA; } @@ -1510,22 +1481,6 @@ static int config_set(int id, GVariant *data, struct sr_dev_inst *sdi, } } - comb_info.comb_addr = comb_base_addr; - if ((ret = command_rd_nvm(usb->devhdl, (unsigned char *)&comb_info, comb_info.comb_addr, sizeof(struct cmd_comb_info))) != SR_OK) { - sr_err("Send Get Comb Command Failed!"); - } else { - if (comb_info.comb_addr == comb_base_addr) { - ret = command_dso_ctrl(usb->devhdl, dso_cmd_gen(sdi, NULL, SR_CONF_COMB_SET)); - if (ret != SR_OK) { - sr_err("Set Comb command failed!"); - return ret; - } - } else { - devc->zero = TRUE; - sr_info("Comb have not been setted!"); - } - } - for(l = sdi->channels; l; l = l->next) { struct sr_channel *probe = (struct sr_channel *)l->data; ret = command_dso_ctrl(usb->devhdl, dso_cmd_gen(sdi, probe, SR_CONF_COUPLING)); @@ -1695,7 +1650,7 @@ static int config_set(int id, GVariant *data, struct sr_dev_inst *sdi, __func__, devc->th_level); } else if (id == SR_CONF_VTH) { devc->vth = g_variant_get_double(data); - ret = command_vth(usb->devhdl, devc->vth); + ret = command_wr_reg(usb->devhdl, (uint8_t)(devc->vth/5.0*255), VTH_ADDR); if (ret == SR_OK) sr_dbg("%s: setting threshold voltage to %d", __func__, devc->vth); @@ -1851,18 +1806,10 @@ static int config_set(int id, GVariant *data, struct sr_dev_inst *sdi, for(l = sdi->channels; l; l = l->next) { struct sr_channel *probe = (struct sr_channel *)l->data; zero_info.zero_addr = zero_base_addr + probe->index * sizeof(struct cmd_zero_info); - zero_info.adc_off = (probe->index == 0) ? mstatus.ch0_adc_off + (mstatus.ch0_adc_sign << 7) : mstatus.ch1_adc_off + (mstatus.ch1_adc_sign << 7); ret = command_wr_nvm(usb->devhdl, (unsigned char *)&zero_info, sizeof(struct cmd_zero_info)); if (ret != SR_OK) sr_err("DSO channel %d Set Zero command failed!", probe->index); } - comb_info.comb_addr = comb_base_addr; - comb_info.comb0_low_off = mstatus.comb0_off; - comb_info.comb0_hig_off = mstatus.comb0_off >> 8; - comb_info.comb1_low_off = mstatus.comb1_off; - comb_info.comb1_hig_off = mstatus.comb1_off >> 8; - comb_info.comb_sign = mstatus.comb_sign; - ret = command_wr_nvm(usb->devhdl, (unsigned char *)&comb_info, sizeof(struct cmd_comb_info)); if (ret != SR_OK) sr_err("DSO Set Comb command failed!"); else @@ -2221,14 +2168,6 @@ static void receive_transfer(struct libusb_transfer *transfer) mstatus.stream_mode = *((const uint32_t*)cur_buf + mstatus_offset/2 + 16/2) & 0x80000000; mstatus.sample_divider = *((const uint32_t*)cur_buf + mstatus_offset/2 + 18/2) & 0x7fffffff; mstatus.sample_divider_tog = *((const uint32_t*)cur_buf + mstatus_offset/2 + 18/2) & 0x80000000; - mstatus.zeroing = (*((const uint16_t*)cur_buf + mstatus_offset + 128) & 0x8000) != 0; - mstatus.ch0_adc_off = *((const uint8_t*)cur_buf + mstatus_offset*2 + 131*2); - mstatus.ch0_adc_sign = *((const uint8_t*)cur_buf + mstatus_offset*2 + 131*2+1); - mstatus.ch1_adc_off = *((const uint8_t*)cur_buf + mstatus_offset*2 + 135*2); - mstatus.ch1_adc_sign = *((const uint8_t*)cur_buf + mstatus_offset*2 + 135*2+1); - mstatus.comb0_off = *((const uint16_t*)cur_buf + mstatus_offset + 136); - mstatus.comb1_off = *((const uint16_t*)cur_buf + mstatus_offset + 137); - mstatus.comb_sign = *((const uint8_t*)cur_buf + mstatus_offset*2 + 138*2); } else { mstatus.vlen = instant_buffer_size; } diff --git a/libsigrok4DSL/hardware/demo/demo.c b/libsigrok4DSL/hardware/demo/demo.c index d39aebf0..e9536f4d 100644 --- a/libsigrok4DSL/hardware/demo/demo.c +++ b/libsigrok4DSL/hardware/demo/demo.c @@ -278,6 +278,7 @@ static const char *probe_names[NUM_PROBES + 1] = { "CH8", "CH9", "CH10", "CH11", "CH12", "CH13", "CH14", "CH15", NULL, + }; /* Private, per-device-instance driver context. */ diff --git a/libsigrok4DSL/hwdriver.c b/libsigrok4DSL/hwdriver.c index 4e437dfd..da3bcd35 100644 --- a/libsigrok4DSL/hwdriver.c +++ b/libsigrok4DSL/hwdriver.c @@ -49,7 +49,6 @@ * * @{ */ - static struct sr_config_info sr_config_info_data[] = { {SR_CONF_CONN, SR_T_CHAR, "conn", "Connection", "Connection", NULL}, diff --git a/libsigrok4DSL/libsigrok.h b/libsigrok4DSL/libsigrok.h index c6c9e01e..12cf5eaa 100644 --- a/libsigrok4DSL/libsigrok.h +++ b/libsigrok4DSL/libsigrok.h @@ -568,10 +568,11 @@ struct sr_channel { uint64_t vdiv; uint16_t vfactor; double vpos; + uint16_t vpos_trans; uint8_t coupling; uint8_t trig_value; - uint16_t vpos_mid; - uint16_t voff_mid; + int8_t comb_diff_top; + int8_t comb_diff_bom; }; /** Structure for groups of channels that have common properties. */ @@ -629,22 +630,6 @@ struct sr_status { gboolean stream_mode; uint32_t sample_divider; gboolean sample_divider_tog; - - gboolean zeroing; - uint16_t ch0_vpos_mid; - uint16_t ch0_voff_mid; - uint16_t ch0_vcntr; - uint16_t ch1_vpos_mid; - uint16_t ch1_voff_mid; - uint16_t ch1_vcntr; - uint8_t ch0_adc_off; - uint8_t ch1_adc_off; - gboolean ch0_adc_sign; - gboolean ch1_adc_sign; - - uint16_t comb0_off; - uint16_t comb1_off; - uint8_t comb_sign; }; enum { @@ -754,9 +739,12 @@ enum { /** Zero */ SR_CONF_ZERO_SET, + SR_CONF_ZERO_LOAD, SR_CONF_COMB_SET, SR_CONF_ZERO, SR_CONF_ZERO_OVER, + SR_CONF_VOCM, + SR_CONF_CALI, /** status for dso channel */ SR_CONF_STATUS_PERIOD, @@ -778,6 +766,13 @@ enum { /** Vertical offset */ SR_CONF_VOFF, + SR_CONF_VOFF_DEFAULT, + SR_CONF_VOFF_RANGE, + + /** VGain */ + SR_CONF_VGAIN, + SR_CONF_VGAIN_DEFAULT, + SR_CONF_VGAIN_RANGE, /** Coupling for dso channel. */ SR_CONF_COUPLING, From b1446b780d6d0b18dacb12e32b988ecdcea7c083 Mon Sep 17 00:00:00 2001 From: DreamSourceLab Date: Fri, 10 Jun 2016 10:02:08 +0800 Subject: [PATCH 21/32] Improve memory alloc and free for each capture --- DSView/darkstyle/style.qss | 7 +- DSView/dsapplication.cpp | 10 +- DSView/icons/wait.gif | Bin 10071 -> 9469 bytes DSView/pv/data/analog.cpp | 6 + DSView/pv/data/analog.h | 1 + DSView/pv/data/analogsnapshot.cpp | 92 ++++++-- DSView/pv/data/analogsnapshot.h | 3 +- DSView/pv/data/decode/annotation.cpp | 6 + DSView/pv/data/decode/annotation.h | 2 + DSView/pv/data/decode/rowdata.h | 1 - DSView/pv/data/decoderstack.cpp | 81 +++++-- DSView/pv/data/decoderstack.h | 14 +- DSView/pv/data/dso.cpp | 7 + DSView/pv/data/dso.h | 1 + DSView/pv/data/dsosnapshot.cpp | 117 +++++++--- DSView/pv/data/dsosnapshot.h | 3 +- DSView/pv/data/group.cpp | 11 +- DSView/pv/data/group.h | 1 + DSView/pv/data/groupsnapshot.cpp | 22 +- DSView/pv/data/groupsnapshot.h | 5 +- DSView/pv/data/logic.cpp | 7 + DSView/pv/data/logic.h | 2 + DSView/pv/data/logicsnapshot.cpp | 91 ++++++-- DSView/pv/data/logicsnapshot.h | 3 +- DSView/pv/data/mathstack.cpp | 3 + DSView/pv/data/mathstack.h | 1 + DSView/pv/data/signaldata.cpp | 1 - DSView/pv/data/signaldata.h | 2 + DSView/pv/data/snapshot.cpp | 64 ++---- DSView/pv/data/snapshot.h | 10 +- DSView/pv/dialogs/fftoptions.cpp | 2 + DSView/pv/dialogs/protocolexp.cpp | 4 +- DSView/pv/dialogs/protocollist.cpp | 4 +- DSView/pv/dialogs/waitingdialog.cpp | 8 +- DSView/pv/dialogs/waitingdialog.h | 2 + DSView/pv/mainwindow.cpp | 5 +- DSView/pv/sigsession.cpp | 258 ++++++++++----------- DSView/pv/sigsession.h | 10 +- DSView/pv/storesession.cpp | 8 +- DSView/pv/storesession.h | 2 +- DSView/pv/toolbars/samplingbar.cpp | 6 +- DSView/pv/toolbars/trigbar.cpp | 5 + DSView/pv/toolbars/trigbar.h | 2 + DSView/pv/view/analogsignal.cpp | 2 +- DSView/pv/view/analogsignal.h | 2 +- DSView/pv/view/decodetrace.cpp | 31 +-- DSView/pv/view/decodetrace.h | 1 + DSView/pv/view/devmode.cpp | 2 +- DSView/pv/view/dsosignal.cpp | 2 +- DSView/pv/view/dsosignal.h | 2 +- DSView/pv/view/logicsignal.cpp | 4 +- DSView/pv/view/logicsignal.h | 4 +- DSView/pv/view/mathtrace.cpp | 4 +- DSView/pv/view/signal.cpp | 11 +- DSView/pv/view/signal.h | 11 +- DSView/pv/view/view.cpp | 13 +- DSView/pv/view/viewport.cpp | 4 + DSView/pv/widgets/decodergroupbox.cpp | 16 +- DSView/pv/widgets/decodergroupbox.h | 2 +- libsigrok4DSL/hardware/DSL/dscope.c | 3 + libsigrok4DSL/hardware/DSL/dsl.h | 1 + libsigrok4DSL/hardware/DSL/dslogic.c | 313 ++++++++++++++------------ libsigrok4DSL/hardware/demo/demo.c | 86 ++++--- libsigrok4DSL/libsigrok.h | 2 +- libsigrok4DSL/session.c | 66 +++--- libsigrok4DSL/trigger.c | 9 +- 66 files changed, 919 insertions(+), 562 deletions(-) diff --git a/DSView/darkstyle/style.qss b/DSView/darkstyle/style.qss index fdb48dec..975afee6 100755 --- a/DSView/darkstyle/style.qss +++ b/DSView/darkstyle/style.qss @@ -495,6 +495,7 @@ QTextEdit background-color: #201F1F; color: silver; border: 1px solid #3A3939; + margin: 0; } QPlainTextEdit @@ -591,7 +592,7 @@ QComboBox border: 1px solid #3A3939; border-radius: 2px; padding: 2px; - min-width: 75px; + min-width: 30px; } QPushButton:checked{ @@ -662,7 +663,7 @@ QAbstractSpinBox { background-color: #201F1F; color: silver; border-radius: 2px; - min-width: 75px; + min-width: 50px; } QAbstractSpinBox:up-button @@ -705,6 +706,8 @@ QAbstractSpinBox::down-arrow:hover QLabel { border: 0px solid black; + margin-left: 2px; + margin-right: 2px; } QTabWidget{ diff --git a/DSView/dsapplication.cpp b/DSView/dsapplication.cpp index 6ac6f80d..9facc855 100644 --- a/DSView/dsapplication.cpp +++ b/DSView/dsapplication.cpp @@ -39,7 +39,13 @@ bool DSApplication::notify(QObject *receiver_, QEvent *event_) msg.setStandardButtons(QMessageBox::Ok); msg.setIcon(QMessageBox::Warning); msg.exec(); - //QMessageBox::warning(NULL, "Application Error", e.what()) - return false; + } catch (...) { + QMessageBox msg(NULL); + msg.setText("Application Error"); + msg.setInformativeText("An unexpected error occurred"); + msg.setStandardButtons(QMessageBox::Ok); + msg.setIcon(QMessageBox::Warning); + msg.exec(); } + return false; } diff --git a/DSView/icons/wait.gif b/DSView/icons/wait.gif index 58279b9f1806d1c6ac1287f5e6cd86b600fce917..b3ba36a619d15ef20c1e7f5087fcfcef2ea452f9 100644 GIT binary patch literal 9469 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 F{ttdz>ZJex diff --git a/DSView/pv/data/analog.cpp b/DSView/pv/data/analog.cpp index 790c12a5..4d3c5ca5 100644 --- a/DSView/pv/data/analog.cpp +++ b/DSView/pv/data/analog.cpp @@ -53,6 +53,12 @@ void Analog::clear() BOOST_FOREACH(const boost::shared_ptr s, _snapshots) s->clear(); } +void Analog::init() +{ + //_snapshots.clear(); + BOOST_FOREACH(const boost::shared_ptr s, _snapshots) + s->init(); +} } // namespace data } // namespace pv diff --git a/DSView/pv/data/analog.h b/DSView/pv/data/analog.h index dc2a8571..c28b649f 100644 --- a/DSView/pv/data/analog.h +++ b/DSView/pv/data/analog.h @@ -46,6 +46,7 @@ public: get_snapshots(); void clear(); + void init(); private: std::deque< boost::shared_ptr > _snapshots; diff --git a/DSView/pv/data/analogsnapshot.cpp b/DSView/pv/data/analogsnapshot.cpp index 4b8c1719..005885c7 100644 --- a/DSView/pv/data/analogsnapshot.cpp +++ b/DSView/pv/data/analogsnapshot.cpp @@ -54,15 +54,41 @@ AnalogSnapshot::AnalogSnapshot() : AnalogSnapshot::~AnalogSnapshot() { - boost::lock_guard lock(_mutex); - BOOST_FOREACH(Envelope &e, _envelope_levels[0]) - free(e.samples); + free_envelop(); +} + +void AnalogSnapshot::free_envelop() +{ + for (unsigned int i = 0; i < _channel_num; i++) { + BOOST_FOREACH(Envelope &e, _envelope_levels[i]) { + if (e.samples) + free(e.samples); + } + } + memset(_envelope_levels, 0, sizeof(_envelope_levels)); +} + +void AnalogSnapshot::init() +{ + boost::lock_guard lock(_mutex); + _sample_count = 0; + _ring_sample_count = 0; + _memory_failed = false; + _last_ended = true; + for (unsigned int i = 0; i < _channel_num; i++) { + for (unsigned int level = 0; level < ScaleStepCount; level++) { + _envelope_levels[i][level].length = 0; + _envelope_levels[i][level].data_length = 0; + } + } } void AnalogSnapshot::clear() { - _sample_count = 0; - _ring_sample_count = 0; + boost::lock_guard lock(_mutex); + free_data(); + free_envelop(); + init(); } void AnalogSnapshot::first_payload(const sr_datafeed_analog &analog, uint64_t total_sample_count, unsigned int channel_num) @@ -70,17 +96,51 @@ void AnalogSnapshot::first_payload(const sr_datafeed_analog &analog, uint64_t to _total_sample_count = total_sample_count; _channel_num = channel_num; _unit_size = sizeof(uint16_t)*channel_num; - boost::lock_guard lock(_mutex); - if (init(_total_sample_count*_channel_num) == SR_OK) { + + bool isOk = true; + uint64_t size = _total_sample_count * _unit_size + sizeof(uint64_t); + if (size != _capacity) { + free_data(); + _data = malloc(size); + if (_data) { + free_envelop(); + for (unsigned int i = 0; i < _channel_num; i++) { + uint64_t envelop_count = _total_sample_count / EnvelopeScaleFactor; + for (unsigned int level = 0; level < ScaleStepCount; level++) { + envelop_count = ((envelop_count + EnvelopeDataUnit - 1) / + EnvelopeDataUnit) * EnvelopeDataUnit; + _envelope_levels[i][level].samples = (EnvelopeSample*)malloc(envelop_count * sizeof(EnvelopeSample)); + if (!_envelope_levels[i][level].samples) { + isOk = false; + break; + } + envelop_count = envelop_count / EnvelopeScaleFactor; + } + if (!isOk) + break; + } + } else { + isOk = true; + } + } + + if (isOk) { + _capacity = size; + _memory_failed = false; append_payload(analog); _last_ended = false; + } else { + free_data(); + free_envelop(); + _capacity = 0; + _memory_failed = true; } } void AnalogSnapshot::append_payload( const sr_datafeed_analog &analog) { - boost::lock_guard lock(_mutex); + boost::lock_guard lock(_mutex); append_data(analog.data, analog.num_samples); // Generate the first mip-map from the data @@ -98,13 +158,11 @@ const uint16_t* AnalogSnapshot::get_samples( assert(end_sample < (int64_t)get_sample_count()); assert(start_sample <= end_sample); - boost::lock_guard lock(_mutex); - // uint16_t *const data = new uint16_t[end_sample - start_sample]; // memcpy(data, (uint16_t*)_data + start_sample, sizeof(uint16_t) * // (end_sample - start_sample)); // return data; - return (uint16_t*)_data.data() + start_sample * _channel_num; + return (uint16_t*)_data + start_sample * _channel_num; } void AnalogSnapshot::get_envelope_section(EnvelopeSection &s, @@ -114,8 +172,6 @@ void AnalogSnapshot::get_envelope_section(EnvelopeSection &s, assert(start <= end); assert(min_length > 0); - boost::lock_guard lock(_mutex); - const unsigned int min_level = max((int)floorf(logf(min_length) / LogEnvelopeScaleFactor) - 1, 0); const unsigned int scale_power = (min_level + 1) * @@ -139,8 +195,8 @@ void AnalogSnapshot::reallocate_envelope(Envelope &e) if (new_data_length > e.data_length) { e.data_length = new_data_length; - e.samples = (EnvelopeSample*)realloc(e.samples, - new_data_length * sizeof(EnvelopeSample)); +// e.samples = (EnvelopeSample*)realloc(e.samples, +// new_data_length * sizeof(EnvelopeSample)); } } @@ -154,7 +210,7 @@ void AnalogSnapshot::append_payload_to_envelope_levels() // Expand the data buffer to fit the new samples prev_length = e0.length; - e0.length = get_sample_count() / EnvelopeScaleFactor; + e0.length = _sample_count / EnvelopeScaleFactor; // Break off if there are no new samples to compute // if (e0.length == prev_length) @@ -169,7 +225,7 @@ void AnalogSnapshot::append_payload_to_envelope_levels() dest_ptr = e0.samples + prev_length; // Iterate through the samples to populate the first level mipmap - const uint16_t *const stop_src_ptr = (uint16_t*)_data.data() + + const uint16_t *const stop_src_ptr = (uint16_t*)_data + e0.length * EnvelopeScaleFactor * _channel_num; // for (const uint16_t *src_ptr = (uint16_t*)_data + // prev_length * EnvelopeScaleFactor; @@ -182,7 +238,7 @@ void AnalogSnapshot::append_payload_to_envelope_levels() // *dest_ptr++ = sub_sample; // } - for (const uint16_t *src_ptr = (uint16_t*)_data.data() + + for (const uint16_t *src_ptr = (uint16_t*)_data + prev_length * EnvelopeScaleFactor * _channel_num + i; src_ptr < stop_src_ptr; src_ptr += EnvelopeScaleFactor * _channel_num) { diff --git a/DSView/pv/data/analogsnapshot.h b/DSView/pv/data/analogsnapshot.h index b7a8813d..3f2b015a 100644 --- a/DSView/pv/data/analogsnapshot.h +++ b/DSView/pv/data/analogsnapshot.h @@ -74,6 +74,7 @@ public: virtual ~AnalogSnapshot(); void clear(); + void init(); void first_payload(const sr_datafeed_analog &analog, uint64_t total_sample_count, unsigned int channel_num); @@ -86,8 +87,8 @@ public: uint64_t start, uint64_t end, float min_length, int probe_index) const; private: + void free_envelop(); void reallocate_envelope(Envelope &l); - void append_payload_to_envelope_levels(); private: diff --git a/DSView/pv/data/decode/annotation.cpp b/DSView/pv/data/decode/annotation.cpp index 1cf20090..f2d30df5 100644 --- a/DSView/pv/data/decode/annotation.cpp +++ b/DSView/pv/data/decode/annotation.cpp @@ -42,6 +42,7 @@ Annotation::Annotation(const srd_proto_data *const pdata) : assert(pda); _format = pda->ann_class; + _type = pda->ann_type; const char *const *annotations = (char**)pda->ann_text; while(*annotations) { @@ -76,6 +77,11 @@ int Annotation::format() const return _format; } +int Annotation::type() const +{ + return _type; +} + const std::vector& Annotation::annotations() const { return _annotations; diff --git a/DSView/pv/data/decode/annotation.h b/DSView/pv/data/decode/annotation.h index cba04c99..03e208ef 100644 --- a/DSView/pv/data/decode/annotation.h +++ b/DSView/pv/data/decode/annotation.h @@ -42,12 +42,14 @@ public: uint64_t start_sample() const; uint64_t end_sample() const; int format() const; + int type() const; const std::vector& annotations() const; private: uint64_t _start_sample; uint64_t _end_sample; int _format; + int _type; std::vector _annotations; }; diff --git a/DSView/pv/data/decode/rowdata.h b/DSView/pv/data/decode/rowdata.h index e2a84f31..40792cb7 100644 --- a/DSView/pv/data/decode/rowdata.h +++ b/DSView/pv/data/decode/rowdata.h @@ -35,7 +35,6 @@ class RowData public: RowData(); ~RowData(); - public: uint64_t get_max_sample() const; diff --git a/DSView/pv/data/decoderstack.cpp b/DSView/pv/data/decoderstack.cpp index c480a006..e0b64ab9 100644 --- a/DSView/pv/data/decoderstack.cpp +++ b/DSView/pv/data/decoderstack.cpp @@ -60,7 +60,7 @@ const int64_t DecoderStack::DecodeChunkLength = 4 * 1024; //const int64_t DecoderStack::DecodeChunkLength = 1024 * 1024; const unsigned int DecoderStack::DecodeNotifyPeriod = 1024; -mutex DecoderStack::_global_decode_mutex; +//mutex DecoderStack::_global_decode_mutex; DecoderStack::DecoderStack(pv::SigSession &session, const srd_decoder *const dec) : @@ -190,7 +190,7 @@ void DecoderStack::build_row() int64_t DecoderStack::samples_decoded() const { - lock_guard decode_lock(_output_mutex); + lock_guard decode_lock(_output_mutex); return _samples_decoded; } @@ -199,7 +199,7 @@ void DecoderStack::get_annotation_subset( const Row &row, uint64_t start_sample, uint64_t end_sample) const { - lock_guard lock(_output_mutex); + //lock_guard lock(_output_mutex); std::map::const_iterator iter = _rows.find(row); @@ -210,7 +210,7 @@ void DecoderStack::get_annotation_subset( uint64_t DecoderStack::get_max_annotation(const Row &row) { - lock_guard lock(_output_mutex); + //lock_guard lock(_output_mutex); std::map::const_iterator iter = _rows.find(row); @@ -222,7 +222,7 @@ uint64_t DecoderStack::get_max_annotation(const Row &row) uint64_t DecoderStack::get_min_annotation(const Row &row) { - lock_guard lock(_output_mutex); + //lock_guard lock(_output_mutex); std::map::const_iterator iter = _rows.find(row); @@ -232,14 +232,24 @@ uint64_t DecoderStack::get_min_annotation(const Row &row) return 0; } -std::map& DecoderStack::get_rows_gshow() +std::map DecoderStack::get_rows_gshow() { - return _rows_gshow; + std::map rows_gshow; + for (std::map::const_iterator i = _rows_gshow.begin(); + i != _rows_gshow.end(); i++) { + rows_gshow[(*i).first] = (*i).second; + } + return rows_gshow; } -std::map& DecoderStack::get_rows_lshow() +std::map DecoderStack::get_rows_lshow() { - return _rows_lshow; + std::map rows_lshow; + for (std::map::const_iterator i = _rows_lshow.begin(); + i != _rows_lshow.end(); i++) { + rows_lshow[(*i).first] = (*i).second; + } + return rows_lshow; } void DecoderStack::set_rows_gshow(const decode::Row row, bool show) @@ -260,7 +270,7 @@ void DecoderStack::set_rows_lshow(const decode::Row row, bool show) bool DecoderStack::has_annotations(const Row &row) const { - lock_guard lock(_output_mutex); + //lock_guard lock(_output_mutex); std::map::const_iterator iter = _rows.find(row); @@ -275,6 +285,7 @@ bool DecoderStack::has_annotations(const Row &row) const uint64_t DecoderStack::list_annotation_size() const { + lock_guard lock(_output_mutex); uint64_t max_annotation_size = 0; int row = 0; for (map::const_iterator i = _rows.begin(); @@ -291,6 +302,7 @@ uint64_t DecoderStack::list_annotation_size() const bool DecoderStack::list_annotation(pv::data::decode::Annotation &ann, uint16_t row_index, uint64_t col_index) const { + //lock_guard lock(_output_mutex); int row = 0; for (map::const_iterator i = _rows.begin(); i != _rows.end(); i++) { @@ -308,6 +320,7 @@ bool DecoderStack::list_annotation(pv::data::decode::Annotation &ann, bool DecoderStack::list_row_title(int row, QString &title) const { + //lock_guard lock(_output_mutex); int index = 0; for (map::const_iterator i = _rows.begin(); i != _rows.end(); i++) { @@ -324,23 +337,33 @@ bool DecoderStack::list_row_title(int row, QString &title) const QString DecoderStack::error_message() { - lock_guard lock(_output_mutex); + //lock_guard lock(_output_mutex); return _error_message; } void DecoderStack::clear() { + //lock_guard decode_lock(_output_mutex); _sample_count = 0; - _frame_complete = false; - _samples_decoded = 0; + _frame_complete = false; + _samples_decoded = 0; new_decode_data(); _error_message = QString(); - for (map::const_iterator i = _rows.begin(); + for (map::iterator i = _rows.begin(); i != _rows.end(); i++) _rows[(*i).first] = decode::RowData(); +// _rows.clear(); +// _rows_gshow.clear(); +// _rows_lshow.clear(); +// _class_rows.clear(); _no_memory = false; } +void DecoderStack::init() +{ + clear(); +} + void DecoderStack::stop_decode() { //_snapshot.reset(); @@ -380,6 +403,9 @@ void DecoderStack::begin_decode() return; } +// // Build rows +// build_row(); + // We get the logic data of the first channel in the list. // This works because we are currently assuming all // LogicSignals have the same data/snapshot @@ -425,10 +451,10 @@ uint64_t DecoderStack::get_max_sample_count() const boost::optional DecoderStack::wait_for_data() const { - unique_lock input_lock(_input_mutex); + //unique_lock input_lock(_input_mutex); while(!boost::this_thread::interruption_requested() && !_frame_complete && (uint64_t)_samples_decoded >= _sample_count) - _input_cond.wait(input_lock); + //_input_cond.wait(input_lock); return boost::make_optional( !boost::this_thread::interruption_requested() && ((uint64_t)_samples_decoded < _sample_count || !_frame_complete), @@ -456,6 +482,7 @@ void DecoderStack::decode_data( } } + uint64_t entry_cnt = 0; uint8_t chunk_type = 0; uint64_t i = decode_start; while(!boost::this_thread::interruption_requested() && @@ -475,6 +502,7 @@ void DecoderStack::decode_data( if (logic_di && logic_di->logic_mask != 0) { uint64_t cur_pos = logic_di->cur_pos; + assert(cur_pos < _snapshot->get_sample_count()); uint64_t sample = _snapshot->get_sample(cur_pos) & logic_di->logic_mask; if (logic_di->edge_index == -1) { std::vector pos_vector; @@ -509,7 +537,7 @@ void DecoderStack::decode_data( } { - lock_guard lock(_output_mutex); + lock_guard lock(_output_mutex); _samples_decoded = i - decode_start + 1; } @@ -517,6 +545,7 @@ void DecoderStack::decode_data( last_cnt = i; new_decode_data(); } + entry_cnt++; } _options_changed = false; decode_done(); @@ -524,7 +553,7 @@ void DecoderStack::decode_data( void DecoderStack::decode_proc() { - lock_guard decode_lock(_global_decode_mutex); + //lock_guard decode_lock(_global_decode_mutex); optional sample_count; srd_session *session; @@ -545,7 +574,7 @@ void DecoderStack::decode_proc() // Get the intial sample count { - unique_lock input_lock(_input_mutex); + //unique_lock input_lock(_input_mutex); sample_count = _sample_count = _snapshot->get_sample_count(); } @@ -610,7 +639,7 @@ void DecoderStack::annotation_callback(srd_proto_data *pdata, void *decoder) DecoderStack *const d = (DecoderStack*)decoder; assert(d); - lock_guard lock(d->_output_mutex); + //lock_guard lock(d->_output_mutex); if (d->_no_memory) return; @@ -638,13 +667,14 @@ void DecoderStack::annotation_callback(srd_proto_data *pdata, void *decoder) assert(row_iter != d->_rows.end()); if (row_iter == d->_rows.end()) { - qDebug() << "Unexpected annotation: decoder = " << decc << - ", format = " << a.format(); - assert(0); - return; - } + qDebug() << "Unexpected annotation: decoder = " << decc << + ", format = " << a.format(); + assert(0); + return; + } // Add the annotation + lock_guard lock(d->_output_mutex); if (!(*row_iter).second.push_annotation(a)) d->_no_memory = true; } @@ -678,6 +708,7 @@ void DecoderStack::on_frame_ended() int DecoderStack::list_rows_size() { + //lock_guard lock(_output_mutex); int rows_size = 0; int row = 0; for (map::const_iterator i = _rows.begin(); diff --git a/DSView/pv/data/decoderstack.h b/DSView/pv/data/decoderstack.h index 7516c907..8f38d361 100644 --- a/DSView/pv/data/decoderstack.h +++ b/DSView/pv/data/decoderstack.h @@ -99,8 +99,8 @@ public: uint64_t get_max_annotation(const decode::Row &row); uint64_t get_min_annotation(const decode::Row &row); // except instant(end=start) annotation - std::map &get_rows_gshow(); - std::map &get_rows_lshow(); + std::map get_rows_gshow(); + std::map get_rows_lshow(); void set_rows_gshow(const decode::Row row, bool show); void set_rows_lshow(const decode::Row row, bool show); @@ -117,6 +117,7 @@ public: QString error_message(); void clear(); + void init(); uint64_t get_max_sample_count() const; @@ -168,18 +169,19 @@ private: * @todo A proper solution should be implemented to allow multiple * decode operations. */ - static boost::mutex _global_decode_mutex; + //static boost::mutex _global_decode_mutex; std::list< boost::shared_ptr > _stack; boost::shared_ptr _snapshot; - mutable boost::mutex _input_mutex; - mutable boost::condition_variable _input_cond; + //mutable boost::mutex _input_mutex; + //mutable boost::condition_variable _input_cond; uint64_t _sample_count; bool _frame_complete; - mutable boost::mutex _output_mutex; + mutable boost::recursive_mutex _output_mutex; + //mutable boost::mutex _output_mutex; int64_t _samples_decoded; std::map _rows; diff --git a/DSView/pv/data/dso.cpp b/DSView/pv/data/dso.cpp index fe3ce699..2a6ce2c7 100644 --- a/DSView/pv/data/dso.cpp +++ b/DSView/pv/data/dso.cpp @@ -53,5 +53,12 @@ void Dso::clear() s->clear(); } +void Dso::init() +{ + //_snapshots.clear(); + BOOST_FOREACH(const boost::shared_ptr s, _snapshots) + s->init(); +} + } // namespace data } // namespace pv diff --git a/DSView/pv/data/dso.h b/DSView/pv/data/dso.h index 0e724ed6..0902972a 100644 --- a/DSView/pv/data/dso.h +++ b/DSView/pv/data/dso.h @@ -45,6 +45,7 @@ public: get_snapshots(); void clear(); + void init(); private: std::deque< boost::shared_ptr > _snapshots; diff --git a/DSView/pv/data/dsosnapshot.cpp b/DSView/pv/data/dsosnapshot.cpp index b4f93c91..0bd6c6c2 100644 --- a/DSView/pv/data/dsosnapshot.cpp +++ b/DSView/pv/data/dsosnapshot.cpp @@ -58,15 +58,42 @@ DsoSnapshot::DsoSnapshot() : DsoSnapshot::~DsoSnapshot() { - boost::lock_guard lock(_mutex); - BOOST_FOREACH(Envelope &e, _envelope_levels[0]) - free(e.samples); + free_envelop(); +} + +void DsoSnapshot::free_envelop() +{ + for (unsigned int i = 0; i < _channel_num; i++) { + BOOST_FOREACH(Envelope &e, _envelope_levels[i]) { + if (e.samples) + free(e.samples); + } + } + memset(_envelope_levels, 0, sizeof(_envelope_levels)); +} + +void DsoSnapshot::init() +{ + boost::lock_guard lock(_mutex); + _sample_count = 0; + _ring_sample_count = 0; + _memory_failed = false; + _last_ended = true; + _envelope_done = false; + for (unsigned int i = 0; i < _channel_num; i++) { + for (unsigned int level = 0; level < ScaleStepCount; level++) { + _envelope_levels[i][level].length = 0; + _envelope_levels[i][level].data_length = 0; + } + } } void DsoSnapshot::clear() { - _sample_count = 0; - _ring_sample_count = 0; + boost::lock_guard lock(_mutex); + free_data(); + free_envelop(); + init(); } void DsoSnapshot::first_payload(const sr_datafeed_dso &dso, uint64_t total_sample_count, unsigned int channel_num, bool instant) @@ -74,18 +101,52 @@ void DsoSnapshot::first_payload(const sr_datafeed_dso &dso, uint64_t total_sampl _total_sample_count = total_sample_count; _channel_num = channel_num; _instant = instant; - boost::lock_guard lock(_mutex); - if (init(_total_sample_count) == SR_OK) { + + bool isOk = true; + uint64_t size = _total_sample_count * _unit_size + sizeof(uint64_t); + if (size != _capacity) { + free_data(); + _data = malloc(size); + if (_data) { + free_envelop(); + for (unsigned int i = 0; i < _channel_num; i++) { + uint64_t envelop_count = _total_sample_count / EnvelopeScaleFactor; + for (unsigned int level = 0; level < ScaleStepCount; level++) { + envelop_count = ((envelop_count + EnvelopeDataUnit - 1) / + EnvelopeDataUnit) * EnvelopeDataUnit; + _envelope_levels[i][level].samples = (EnvelopeSample*)malloc(envelop_count * sizeof(EnvelopeSample)); + if (!_envelope_levels[i][level].samples) { + isOk = false; + break; + } + envelop_count = envelop_count / EnvelopeScaleFactor; + } + if (!isOk) + break; + } + } else { + isOk = true; + } + } + + if (isOk) { + _capacity = size; + _memory_failed = false; append_payload(dso); _last_ended = false; + } else { + free_data(); + free_envelop(); + _capacity = 0; + _memory_failed = true; } } void DsoSnapshot::append_payload(const sr_datafeed_dso &dso) { - boost::lock_guard lock(_mutex); + boost::lock_guard lock(_mutex); - if (_channel_num > 0) { + if (_channel_num > 0 && dso.num_samples != 0) { refill_data(dso.data, dso.num_samples, _instant); // Generate the first mip-map from the data @@ -96,6 +157,7 @@ void DsoSnapshot::append_payload(const sr_datafeed_dso &dso) void DsoSnapshot::enable_envelope(bool enable) { + boost::lock_guard lock(_mutex); if (!_envelope_done && enable) append_payload_to_envelope_levels(true); _envelope_en = enable; @@ -112,13 +174,11 @@ const uint8_t *DsoSnapshot::get_samples( assert(end_sample < (int64_t)get_sample_count()); assert(start_sample <= end_sample); - boost::lock_guard lock(_mutex); - // uint16_t *const data = new uint16_t[end_sample - start_sample]; // memcpy(data, (uint16_t*)_data + start_sample, sizeof(uint16_t) * // (end_sample - start_sample)); // return data; - return (uint8_t*)_data.data() + start_sample * _channel_num + index * (_channel_num != 1); + return (uint8_t*)_data + start_sample * _channel_num + index * (_channel_num != 1); } void DsoSnapshot::get_envelope_section(EnvelopeSection &s, @@ -128,7 +188,10 @@ void DsoSnapshot::get_envelope_section(EnvelopeSection &s, assert(start <= end); assert(min_length > 0); - boost::lock_guard lock(_mutex); + if (!_envelope_done) { + s.length = 0; + return; + } const unsigned int min_level = max((int)floorf(logf(min_length) / LogEnvelopeScaleFactor) - 1, 0); @@ -156,8 +219,8 @@ void DsoSnapshot::reallocate_envelope(Envelope &e) if (new_data_length > e.data_length) { e.data_length = new_data_length; - e.samples = (EnvelopeSample*)realloc(e.samples, - new_data_length * sizeof(EnvelopeSample)); +// e.samples = (EnvelopeSample*)realloc(e.samples, +// new_data_length * sizeof(EnvelopeSample)); } } @@ -185,9 +248,9 @@ void DsoSnapshot::append_payload_to_envelope_levels(bool header) dest_ptr = e0.samples + prev_length; // Iterate through the samples to populate the first level mipmap - const uint8_t *const stop_src_ptr = (uint8_t*)_data.data() + + const uint8_t *const stop_src_ptr = (uint8_t*)_data + e0.length * EnvelopeScaleFactor * _channel_num; - for (const uint8_t *src_ptr = (uint8_t*)_data.data() + + for (const uint8_t *src_ptr = (uint8_t*)_data + prev_length * EnvelopeScaleFactor * _channel_num + i; src_ptr < stop_src_ptr; src_ptr += EnvelopeScaleFactor * _channel_num) { @@ -255,7 +318,7 @@ void DsoSnapshot::append_payload_to_envelope_levels(bool header) double DsoSnapshot::cal_vrms(double zero_off, int index) const { assert(index >= 0); - assert(index < _channel_num); + //assert(index < _channel_num); // root-meam-squart value double vrms_pre = 0; @@ -263,9 +326,9 @@ double DsoSnapshot::cal_vrms(double zero_off, int index) const double tmp; // Iterate through the samples to populate the first level mipmap - const uint8_t *const stop_src_ptr = (uint8_t*)_data.data() + - _sample_count * _channel_num; - for (const uint8_t *src_ptr = (uint8_t*)_data.data() + index; + const uint8_t *const stop_src_ptr = (uint8_t*)_data + + get_sample_count() * _channel_num; + for (const uint8_t *src_ptr = (uint8_t*)_data + (index % _channel_num); src_ptr < stop_src_ptr; src_ptr += VrmsScaleFactor * _channel_num) { const uint8_t * begin_src_ptr = @@ -279,7 +342,7 @@ double DsoSnapshot::cal_vrms(double zero_off, int index) const vrms += tmp * tmp; begin_src_ptr += _channel_num; } - vrms = vrms_pre + vrms / _sample_count; + vrms = vrms_pre + vrms / get_sample_count(); vrms_pre = vrms; } vrms = std::pow(vrms, 0.5); @@ -290,16 +353,16 @@ double DsoSnapshot::cal_vrms(double zero_off, int index) const double DsoSnapshot::cal_vmean(int index) const { assert(index >= 0); - assert(index < _channel_num); + //assert(index < _channel_num); // mean value double vmean_pre = 0; double vmean = 0; // Iterate through the samples to populate the first level mipmap - const uint8_t *const stop_src_ptr = (uint8_t*)_data.data() + - _sample_count * _channel_num; - for (const uint8_t *src_ptr = (uint8_t*)_data.data() + index; + const uint8_t *const stop_src_ptr = (uint8_t*)_data + + get_sample_count() * _channel_num; + for (const uint8_t *src_ptr = (uint8_t*)_data + (index % _channel_num); src_ptr < stop_src_ptr; src_ptr += VrmsScaleFactor * _channel_num) { const uint8_t * begin_src_ptr = @@ -312,7 +375,7 @@ double DsoSnapshot::cal_vmean(int index) const vmean += *begin_src_ptr; begin_src_ptr += _channel_num; } - vmean = vmean_pre + vmean / _sample_count; + vmean = vmean_pre + vmean / get_sample_count(); vmean_pre = vmean; } diff --git a/DSView/pv/data/dsosnapshot.h b/DSView/pv/data/dsosnapshot.h index e1488374..df6376e0 100644 --- a/DSView/pv/data/dsosnapshot.h +++ b/DSView/pv/data/dsosnapshot.h @@ -75,6 +75,7 @@ public: virtual ~DsoSnapshot(); void clear(); + void init(); void first_payload(const sr_datafeed_dso &dso, uint64_t total_sample_count, unsigned int channel_num, bool instant); @@ -92,8 +93,8 @@ public: double cal_vmean(int index) const; private: + void free_envelop(); void reallocate_envelope(Envelope &l); - void append_payload_to_envelope_levels(bool header); private: diff --git a/DSView/pv/data/group.cpp b/DSView/pv/data/group.cpp index 3d086e08..7d027db3 100644 --- a/DSView/pv/data/group.cpp +++ b/DSView/pv/data/group.cpp @@ -23,6 +23,8 @@ #include "group.h" #include "groupsnapshot.h" +#include + using namespace boost; using namespace std; @@ -46,7 +48,14 @@ deque< boost::shared_ptr >& Group::get_snapshots() void Group::clear() { - _snapshots.clear(); + BOOST_FOREACH(const boost::shared_ptr s, _snapshots) + s->clear(); +} + +void Group::init() +{ + BOOST_FOREACH(const boost::shared_ptr s, _snapshots) + s->init(); } } // namespace data diff --git a/DSView/pv/data/group.h b/DSView/pv/data/group.h index f166bf94..1312c6ee 100644 --- a/DSView/pv/data/group.h +++ b/DSView/pv/data/group.h @@ -45,6 +45,7 @@ public: get_snapshots(); void clear(); + void init(); private: std::deque< boost::shared_ptr > _snapshots; diff --git a/DSView/pv/data/groupsnapshot.cpp b/DSView/pv/data/groupsnapshot.cpp index 6f0026dd..e7cac5d3 100644 --- a/DSView/pv/data/groupsnapshot.cpp +++ b/DSView/pv/data/groupsnapshot.cpp @@ -54,7 +54,7 @@ GroupSnapshot::GroupSnapshot(const boost::shared_ptr &_logic_snap { assert(_logic_snapshot); - boost::lock_guard lock(_mutex); + //boost::lock_guard lock(_mutex); memset(_envelope_levels, 0, sizeof(_envelope_levels)); _data = _logic_snapshot->get_data(); _sample_count = _logic_snapshot->get_sample_count(); @@ -96,20 +96,30 @@ GroupSnapshot::GroupSnapshot(const boost::shared_ptr &_logic_snap GroupSnapshot::~GroupSnapshot() { - boost::lock_guard lock(_mutex); + //boost::lock_guard lock(_mutex); BOOST_FOREACH(Envelope &e, _envelope_levels) free(e.samples); } +void GroupSnapshot::init() +{ + +} + +void GroupSnapshot::clear() +{ + +} + uint64_t GroupSnapshot::get_sample_count() const { - boost::lock_guard lock(_mutex); + //boost::lock_guard lock(_mutex); return _sample_count; } void GroupSnapshot::append_payload() { - boost::lock_guard lock(_mutex); + //boost::lock_guard lock(_mutex); // Generate the first mip-map from the data append_payload_to_envelope_levels(); @@ -126,7 +136,7 @@ const uint16_t* GroupSnapshot::get_samples( int64_t i; uint16_t tmpl, tmpr; - boost::lock_guard lock(_mutex); + //boost::lock_guard lock(_mutex); uint16_t *const data = new uint16_t[end_sample - start_sample]; // memcpy(data, (uint16_t*)_data + start_sample, sizeof(uint16_t) * @@ -155,7 +165,7 @@ void GroupSnapshot::get_envelope_section(EnvelopeSection &s, assert(start <= end); assert(min_length > 0); - boost::lock_guard lock(_mutex); + //boost::lock_guard lock(_mutex); const unsigned int min_level = max((int)floorf(logf(min_length) / LogEnvelopeScaleFactor) - 1, 0); diff --git a/DSView/pv/data/groupsnapshot.h b/DSView/pv/data/groupsnapshot.h index 6ad65522..4d87b001 100644 --- a/DSView/pv/data/groupsnapshot.h +++ b/DSView/pv/data/groupsnapshot.h @@ -79,6 +79,9 @@ public: virtual ~GroupSnapshot(); + void clear(); + void init(); + void append_payload(); uint64_t get_sample_count() const; @@ -96,7 +99,7 @@ private: private: struct Envelope _envelope_levels[ScaleStepCount]; - mutable boost::recursive_mutex _mutex; + //mutable boost::recursive_mutex _mutex; const void *_data; uint64_t _sample_count; int _unit_size; diff --git a/DSView/pv/data/logic.cpp b/DSView/pv/data/logic.cpp index f2a7cee4..ee499f43 100644 --- a/DSView/pv/data/logic.cpp +++ b/DSView/pv/data/logic.cpp @@ -55,5 +55,12 @@ void Logic::clear() s->clear(); } +void Logic::init() +{ + //_snapshots.clear(); + BOOST_FOREACH(const boost::shared_ptr s, _snapshots) + s->init(); +} + } // namespace data } // namespace pv diff --git a/DSView/pv/data/logic.h b/DSView/pv/data/logic.h index fe815264..f163b35c 100644 --- a/DSView/pv/data/logic.h +++ b/DSView/pv/data/logic.h @@ -47,6 +47,8 @@ public: void clear(); + void init(); + private: std::deque< boost::shared_ptr > _snapshots; }; diff --git a/DSView/pv/data/logicsnapshot.cpp b/DSView/pv/data/logicsnapshot.cpp index 17da83c7..40ee3087 100644 --- a/DSView/pv/data/logicsnapshot.cpp +++ b/DSView/pv/data/logicsnapshot.cpp @@ -54,16 +54,37 @@ LogicSnapshot::LogicSnapshot() : LogicSnapshot::~LogicSnapshot() { - boost::lock_guard lock(_mutex); - BOOST_FOREACH(MipMapLevel &l, _mip_map) - free(l.data); + free_mipmap(); +} + +void LogicSnapshot::free_mipmap() +{ + BOOST_FOREACH(MipMapLevel &l, _mip_map) { + if (l.data) + free(l.data); + } + memset(_mip_map, 0, sizeof(_mip_map)); +} + +void LogicSnapshot::init() +{ + boost::lock_guard lock(_mutex); + _sample_count = 0; + _ring_sample_count = 0; + _memory_failed = false; + _last_ended = true; + for (unsigned int level = 0; level < ScaleStepCount; level++) { + _mip_map[level].length = 0; + _mip_map[level].data_length = 0; + } } void LogicSnapshot::clear() { - _sample_count = 0; - _ring_sample_count = 0; - memset(_mip_map, 0, sizeof(_mip_map)); + boost::lock_guard lock(_mutex); + free_data(); + free_mipmap(); + init(); } void LogicSnapshot::first_payload(const sr_datafeed_logic &logic, uint64_t total_sample_count, unsigned int channel_num) @@ -71,10 +92,40 @@ void LogicSnapshot::first_payload(const sr_datafeed_logic &logic, uint64_t total _total_sample_count = total_sample_count; _channel_num = channel_num; _unit_size = logic.unitsize; - boost::lock_guard lock(_mutex); - if (init(_total_sample_count * _channel_num) == SR_OK) { + + bool isOk = true; + uint64_t size = _total_sample_count * _unit_size + sizeof(uint64_t); + if (size != _capacity) { + free_data(); + _data = malloc(size); + if (_data) { + free_mipmap(); + uint64_t mipmap_count = _total_sample_count / MipMapScaleFactor; + for (unsigned int level = 0; level < ScaleStepCount; level++) { + mipmap_count = ((mipmap_count + MipMapDataUnit - 1) / + MipMapDataUnit) * MipMapDataUnit; + _mip_map[level].data = malloc(mipmap_count * _unit_size + sizeof(uint64_t)); + if (!_mip_map[level].data) { + isOk = false; + break; + } + mipmap_count = mipmap_count / MipMapScaleFactor; + } + } else { + isOk = false; + } + } + + if (isOk) { + _capacity = size; + _memory_failed = false; append_payload(logic); _last_ended = false; + } else { + free_data(); + free_mipmap(); + _capacity = 0; + _memory_failed = true; } } @@ -84,7 +135,7 @@ void LogicSnapshot::append_payload( assert(_unit_size == logic.unitsize); assert((logic.length % _unit_size) == 0); - boost::lock_guard lock(_mutex); + boost::lock_guard lock(_mutex); append_data(logic.data, logic.length / _unit_size); @@ -96,17 +147,16 @@ uint8_t * LogicSnapshot::get_samples(int64_t start_sample, int64_t end_sample) c { //assert(data); assert(start_sample >= 0); - assert(start_sample <= (int64_t)_sample_count); + assert(start_sample <= (int64_t)get_sample_count()); assert(end_sample >= 0); - assert(end_sample <= (int64_t)_sample_count); + assert(end_sample <= (int64_t)get_sample_count()); assert(start_sample <= end_sample); (void)end_sample; - //lock_guard lock(_mutex); //const size_t size = (end_sample - start_sample) * _unit_size; //memcpy(data, (const uint8_t*)_data + start_sample * _unit_size, size); - return (uint8_t*)_data.data() + start_sample * _unit_size; + return (uint8_t*)_data + start_sample * _unit_size; } void LogicSnapshot::reallocate_mipmap_level(MipMapLevel &m) @@ -118,8 +168,8 @@ void LogicSnapshot::reallocate_mipmap_level(MipMapLevel &m) m.data_length = new_data_length; // Padding is added to allow for the uint64_t write word - m.data = realloc(m.data, new_data_length * _unit_size + - sizeof(uint64_t)); +// m.data = realloc(m.data, new_data_length * _unit_size + +// sizeof(uint64_t)); } } @@ -145,9 +195,9 @@ void LogicSnapshot::append_payload_to_mipmap() dest_ptr = (uint8_t*)m0.data + prev_length * _unit_size; // Iterate through the samples to populate the first level mipmap - const uint8_t *const end_src_ptr = (uint8_t*)_data.data() + + const uint8_t *const end_src_ptr = (uint8_t*)_data + m0.length * _unit_size * MipMapScaleFactor; - for (src_ptr = (uint8_t*)_data.data() + + for (src_ptr = (uint8_t*)_data + prev_length * _unit_size * MipMapScaleFactor; src_ptr < end_src_ptr;) { @@ -213,16 +263,15 @@ void LogicSnapshot::get_subsampled_edges( if (!edges.empty()) edges.clear(); - if (_sample_count == 0) + if (get_sample_count() == 0) return; - assert(end < _sample_count); + assert(end < get_sample_count()); assert(start <= end); assert(min_length > 0); assert(sig_index >= 0); assert(sig_index < 64); - boost::lock_guard lock(_mutex); uint64_t index = start; bool last_sample; const uint64_t block_length = (uint64_t)max(min_length, 1.0f); @@ -573,7 +622,7 @@ uint64_t LogicSnapshot::get_subsample(int level, uint64_t offset) const { assert(level >= 0); assert(_mip_map[level].data); - return *(uint64_t*)((uint8_t*)_mip_map[level].data + + return *(uint64_t*)((uint8_t*)_mip_map[level].data + _unit_size * offset); } diff --git a/DSView/pv/data/logicsnapshot.h b/DSView/pv/data/logicsnapshot.h index 475d8bf0..d1f70635 100644 --- a/DSView/pv/data/logicsnapshot.h +++ b/DSView/pv/data/logicsnapshot.h @@ -66,6 +66,7 @@ public: virtual ~LogicSnapshot(); void clear(); + void init(); void first_payload(const sr_datafeed_logic &logic, uint64_t total_sample_count, unsigned int channel_num); @@ -74,8 +75,8 @@ public: uint8_t * get_samples(int64_t start_sample, int64_t end_sample) const; private: + void free_mipmap(); void reallocate_mipmap_level(MipMapLevel &m); - void append_payload_to_mipmap(); public: diff --git a/DSView/pv/data/mathstack.cpp b/DSView/pv/data/mathstack.cpp index 13aa18d6..69f33e2c 100644 --- a/DSView/pv/data/mathstack.cpp +++ b/DSView/pv/data/mathstack.cpp @@ -72,7 +72,10 @@ MathStack::~MathStack() void MathStack::clear() { +} +void MathStack::init() +{ } int MathStack::get_index() const diff --git a/DSView/pv/data/mathstack.h b/DSView/pv/data/mathstack.h index 6616ac44..d9e32423 100644 --- a/DSView/pv/data/mathstack.h +++ b/DSView/pv/data/mathstack.h @@ -66,6 +66,7 @@ public: MathStack(pv::SigSession &_session, int index); virtual ~MathStack(); void clear(); + void init(); int get_index() const; diff --git a/DSView/pv/data/signaldata.cpp b/DSView/pv/data/signaldata.cpp index afb7f240..00c7e5ef 100644 --- a/DSView/pv/data/signaldata.cpp +++ b/DSView/pv/data/signaldata.cpp @@ -42,7 +42,6 @@ void SignalData::set_samplerate(double samplerate) { assert(samplerate > 0); _samplerate = samplerate; - clear(); } double SignalData::get_start_time() const diff --git a/DSView/pv/data/signaldata.h b/DSView/pv/data/signaldata.h index 464215da..7f96312e 100644 --- a/DSView/pv/data/signaldata.h +++ b/DSView/pv/data/signaldata.h @@ -40,6 +40,8 @@ public: virtual void clear() = 0; + virtual void init() = 0; + double get_start_time() const; protected: diff --git a/DSView/pv/data/snapshot.cpp b/DSView/pv/data/snapshot.cpp index 37eb9acf..df2c8011 100644 --- a/DSView/pv/data/snapshot.cpp +++ b/DSView/pv/data/snapshot.cpp @@ -35,45 +35,31 @@ namespace pv { namespace data { Snapshot::Snapshot(int unit_size, uint64_t total_sample_count, unsigned int channel_num) : + _data(NULL), + _capacity(0), _channel_num(channel_num), _sample_count(0), _total_sample_count(total_sample_count), _ring_sample_count(0), _unit_size(unit_size), - _memory_failed(true), + _memory_failed(false), _last_ended(true) { - boost::lock_guard lock(_mutex); - assert(_unit_size > 0); + assert(_unit_size > 0); } Snapshot::~Snapshot() { - boost::lock_guard lock(_mutex); - _data.clear(); + free_data(); } -int Snapshot::init(uint64_t _total_sample_len) +void Snapshot::free_data() { -// boost::lock_guard lock(_mutex); -// _data = malloc(_total_sample_len * _unit_size + -// sizeof(uint64_t)); - -// if (_data == NULL) -// return SR_ERR_MALLOC; -// else -// return SR_OK; - - boost::lock_guard lock(_mutex); - uint64_t size = _total_sample_len * _unit_size + sizeof(uint64_t); - try{ - _data.resize(size); - } catch(...) { - _memory_failed = true; - return SR_ERR_MALLOC; + if (_data) { + free(_data); + _data = NULL; + _capacity = 0; } - _memory_failed = false; - return SR_OK; } bool Snapshot::memory_failed() const @@ -83,7 +69,7 @@ bool Snapshot::memory_failed() const bool Snapshot::empty() const { - if (_sample_count == 0 || _memory_failed) + if (get_sample_count() == 0 || _memory_failed || !_data) return true; else return false; @@ -101,41 +87,35 @@ void Snapshot::set_last_ended(bool ended) uint64_t Snapshot::get_sample_count() const { - boost::lock_guard lock(_mutex); + boost::lock_guard lock(_mutex); return _sample_count; } const void* Snapshot::get_data() const { - boost::lock_guard lock(_mutex); - return _data.data(); + return _data; } int Snapshot::unit_size() const { - boost::lock_guard lock(_mutex); return _unit_size; } unsigned int Snapshot::get_channel_num() const { - boost::lock_guard lock(_mutex); return _channel_num; } uint64_t Snapshot::get_sample(uint64_t index) const { - boost::lock_guard lock(_mutex); + assert(_data); + assert(index < get_sample_count()); - assert(_data.data()); - assert(index < _sample_count); - - return *(uint64_t*)((uint8_t*)_data.data() + index * _unit_size); + return *(uint64_t*)((uint8_t*)_data + index * _unit_size); } void Snapshot::append_data(void *data, uint64_t samples) { - boost::lock_guard lock(_mutex); // _data = realloc(_data, (_sample_count + samples) * _unit_size + // sizeof(uint64_t)); if (_sample_count + samples < _total_sample_count) @@ -144,13 +124,13 @@ void Snapshot::append_data(void *data, uint64_t samples) _sample_count = _total_sample_count; if (_ring_sample_count + samples > _total_sample_count) { - memcpy((uint8_t*)_data.data() + _ring_sample_count * _unit_size, + memcpy((uint8_t*)_data + _ring_sample_count * _unit_size, data, (_total_sample_count - _ring_sample_count) * _unit_size); _ring_sample_count = (samples + _ring_sample_count - _total_sample_count) % _total_sample_count; - memcpy((uint8_t*)_data.data(), + memcpy((uint8_t*)_data, data, _ring_sample_count * _unit_size); } else { - memcpy((uint8_t*)_data.data() + _ring_sample_count * _unit_size, + memcpy((uint8_t*)_data + _ring_sample_count * _unit_size, data, samples * _unit_size); _ring_sample_count += samples; } @@ -158,13 +138,11 @@ void Snapshot::append_data(void *data, uint64_t samples) void Snapshot::refill_data(void *data, uint64_t samples, bool instant) { - boost::lock_guard lock(_mutex); - if (instant) { - memcpy((uint8_t*)_data.data() + _sample_count * _channel_num, data, samples*_channel_num); + memcpy((uint8_t*)_data + _sample_count * _channel_num, data, samples*_channel_num); _sample_count = (_sample_count + samples) % (_total_sample_count + 1); } else { - memcpy((uint8_t*)_data.data(), data, samples*_channel_num); + memcpy((uint8_t*)_data, data, samples*_channel_num); _sample_count = samples; } diff --git a/DSView/pv/data/snapshot.h b/DSView/pv/data/snapshot.h index ca0e8831..5fa3e440 100644 --- a/DSView/pv/data/snapshot.h +++ b/DSView/pv/data/snapshot.h @@ -38,7 +38,8 @@ public: virtual ~Snapshot(); - int init(uint64_t _total_sample_len); + virtual void clear() = 0; + virtual void init() = 0; uint64_t get_sample_count() const; @@ -59,10 +60,13 @@ public: protected: void append_data(void *data, uint64_t samples); void refill_data(void *data, uint64_t samples, bool instant); + void free_data(); protected: - mutable boost::recursive_mutex _mutex; - std::vector _data; + mutable boost::recursive_mutex _mutex; + //std::vector _data; + void* _data; + uint64_t _capacity; unsigned int _channel_num; uint64_t _sample_count; uint64_t _total_sample_count; diff --git a/DSView/pv/dialogs/fftoptions.cpp b/DSView/pv/dialogs/fftoptions.cpp index ed7280b8..d2344ca3 100644 --- a/DSView/pv/dialogs/fftoptions.cpp +++ b/DSView/pv/dialogs/fftoptions.cpp @@ -118,6 +118,8 @@ FftOptions::FftOptions(QWidget *parent, SigSession &session) : _view_combobox->addItem(view_modes[i], qVariantFromValue(i)); } + assert(_view_combobox->count() > 0); + _view_combobox->setCurrentIndex(_view_combobox->count()-1); for (int i = 0; i < dbv_ranges.size(); i++) { _dbv_combobox->addItem(QString::number(dbv_ranges[i]), diff --git a/DSView/pv/dialogs/protocolexp.cpp b/DSView/pv/dialogs/protocolexp.cpp index ab79796b..af0ef69c 100644 --- a/DSView/pv/dialogs/protocolexp.cpp +++ b/DSView/pv/dialogs/protocolexp.cpp @@ -65,7 +65,7 @@ ProtocolExp::ProtocolExp(QWidget *parent, SigSession &session) : const boost::shared_ptr& decoder_stack = decoder_model->getDecoderStack(); if (decoder_stack) { int row_index = 0; - const std::map& rows(decoder_stack->get_rows_lshow()); + const std::map rows = decoder_stack->get_rows_lshow(); for (std::map::const_iterator i = rows.begin(); i != rows.end(); i++) { if ((*i).second) { @@ -149,7 +149,7 @@ void ProtocolExp::accept() const boost::shared_ptr& decoder_stack = decoder_model->getDecoderStack(); int row_index = 0; Row row; - const std::map& rows_lshow(decoder_stack->get_rows_lshow()); + const std::map rows_lshow = decoder_stack->get_rows_lshow(); for (std::map::const_iterator i = rows_lshow.begin(); i != rows_lshow.end(); i++) { if ((*i).second) { diff --git a/DSView/pv/dialogs/protocollist.cpp b/DSView/pv/dialogs/protocollist.cpp index eb605476..da2451a0 100644 --- a/DSView/pv/dialogs/protocollist.cpp +++ b/DSView/pv/dialogs/protocollist.cpp @@ -128,7 +128,7 @@ void ProtocolList::set_protocol(int index) _session.get_decoder_model()->setDecoderStack(decoder_stack); int row_index = 0; - const std::map& rows(decoder_stack->get_rows_lshow()); + const std::map rows = decoder_stack->get_rows_lshow(); for (std::map::const_iterator i = rows.begin(); i != rows.end(); i++) { QLabel *row_label = new QLabel((*i).first.title(), this); @@ -166,7 +166,7 @@ void ProtocolList::on_row_check(bool show) if (!decoder_stack) return; - std::map& rows(decoder_stack->get_rows_lshow()); + std::map rows = decoder_stack->get_rows_lshow(); for (std::map::const_iterator i = rows.begin(); i != rows.end(); i++) { if (index-- == 0) { diff --git a/DSView/pv/dialogs/waitingdialog.cpp b/DSView/pv/dialogs/waitingdialog.cpp index cb0c742d..1bd53daa 100644 --- a/DSView/pv/dialogs/waitingdialog.cpp +++ b/DSView/pv/dialogs/waitingdialog.cpp @@ -48,15 +48,15 @@ WaitingDialog::WaitingDialog(QWidget *parent, boost::shared_ptrsetFixedSize((GIF_SIZE+TIP_WIDTH)*2, (GIF_SIZE+TIP_HEIGHT)*2); + this->setFixedSize((GIF_WIDTH+TIP_WIDTH)*1.2, (GIF_HEIGHT+TIP_HEIGHT)*4); int midx = this->width() / 2; int midy = this->height() / 2; - this->setWindowOpacity(0.7); + this->setWindowOpacity(0.5); this->setWindowFlags(Qt::Dialog | Qt::FramelessWindowHint); label = new QLabel(this); label->setStyleSheet("background-color: transparent;"); - label->setGeometry(midx-GIF_SIZE/2, midy-GIF_SIZE/2, GIF_SIZE, GIF_SIZE); + label->setGeometry(midx-GIF_WIDTH/2, midy-GIF_HEIGHT/2, GIF_WIDTH, GIF_HEIGHT); movie = new QMovie(":/icons/wait.gif"); label->setMovie(movie); @@ -66,7 +66,7 @@ WaitingDialog::WaitingDialog(QWidget *parent, boost::shared_ptrsetFont(font); - tips->setGeometry(midx-TIP_WIDTH/2, midy+GIF_SIZE/2, TIP_WIDTH, TIP_HEIGHT); + tips->setGeometry(midx-TIP_WIDTH/2, midy+GIF_HEIGHT/2, TIP_WIDTH, TIP_HEIGHT); index = 0; timer = new QTimer(); diff --git a/DSView/pv/dialogs/waitingdialog.h b/DSView/pv/dialogs/waitingdialog.h index 4b3480f6..7693dd78 100644 --- a/DSView/pv/dialogs/waitingdialog.h +++ b/DSView/pv/dialogs/waitingdialog.h @@ -42,6 +42,8 @@ class WaitingDialog : public QDialog private: static const int GIF_SIZE = 80; + static const int GIF_WIDTH = 220; + static const int GIF_HEIGHT = 20; static const int TIP_WIDTH = 100; static const int TIP_HEIGHT = 40; static const int WPOINTS_NUM = 6; diff --git a/DSView/pv/mainwindow.cpp b/DSView/pv/mainwindow.cpp index fccd54af..7dd2e349 100644 --- a/DSView/pv/mainwindow.cpp +++ b/DSView/pv/mainwindow.cpp @@ -543,6 +543,7 @@ void MainWindow::on_trigger(bool visible) _trigger_dock->setVisible(false); _dso_trigger_dock->setVisible(visible); } + _trig_bar->update_trig_btn(visible); } void MainWindow::commit_trigger(bool instant) @@ -689,7 +690,8 @@ bool MainWindow::load_session(QString name) if (!isEnabled) probe->enabled = false; } - _session.init_signals(); + //_session.init_signals(); + _session.reload(); // load signal setting BOOST_FOREACH(const boost::shared_ptr s, _session.get_signals()) { @@ -698,6 +700,7 @@ bool MainWindow::load_session(QString name) if ((s->get_index() == obj["index"].toDouble()) && (s->get_type() == obj["type"].toDouble())) { s->set_colour(QColor(obj["colour"].toString())); + s->set_name(g_strdup(obj["name"].toString().toStdString().c_str())); boost::shared_ptr logicSig; if (logicSig = dynamic_pointer_cast(s)) { diff --git a/DSView/pv/sigsession.cpp b/DSView/pv/sigsession.cpp index 6584c09b..53eca6cd 100644 --- a/DSView/pv/sigsession.cpp +++ b/DSView/pv/sigsession.cpp @@ -98,13 +98,26 @@ SigSession::SigSession(DeviceManager &device_manager) : _group_cnt = 0; register_hotplug_callback(); _view_timer.stop(); - _view_timer.setSingleShot(true); _refresh_timer.stop(); _refresh_timer.setSingleShot(true); _data_lock = false; + _data_updated = false; _decoder_model = new pv::data::DecoderModel(this); - connect(this, SIGNAL(start_timer(int)), &_view_timer, SLOT(start(int))); - //connect(&_view_timer, SIGNAL(timeout()), this, SLOT(refresh())); + + // Create snapshots & data containers + _cur_logic_snapshot.reset(new data::LogicSnapshot()); + _logic_data.reset(new data::Logic()); + _logic_data->push_snapshot(_cur_logic_snapshot); + _cur_dso_snapshot.reset(new data::DsoSnapshot()); + _dso_data.reset(new data::Dso()); + _dso_data->push_snapshot(_cur_dso_snapshot); + _cur_analog_snapshot.reset(new data::AnalogSnapshot()); + _analog_data.reset(new data::Analog()); + _analog_data->push_snapshot(_cur_analog_snapshot); + _group_data.reset(new data::Group()); + _group_cnt = 0; + + connect(&_view_timer, SIGNAL(timeout()), this, SLOT(check_update())); connect(&_refresh_timer, SIGNAL(timeout()), this, SLOT(data_unlock())); } @@ -135,7 +148,7 @@ void SigSession::set_device(boost::shared_ptr dev_inst) throw(Q using pv::device::Device; // Ensure we are not capturing before setting the device - stop_capture(); + //stop_capture(); if (_dev_inst) { sr_session_datafeed_callback_remove_all(); @@ -414,14 +427,14 @@ void SigSession::release_device(device::DevInst *dev_inst) (void)dev_inst; assert(_dev_inst.get() == dev_inst); - assert(_capture_state != Running); + assert(get_capture_state() != Running); _dev_inst = boost::shared_ptr(); //_dev_inst.reset(); } SigSession::capture_state SigSession::get_capture_state() const { - boost::lock_guard lock(_sampling_mutex); + boost::lock_guard lock(_sampling_mutex); return _capture_state; } @@ -443,10 +456,55 @@ double SigSession::cur_sampletime() const return _cur_samplelimits * 1.0 / _cur_samplerate; } +void SigSession::capture_init() +{ + _cur_samplerate = _dev_inst->get_sample_rate(); + _cur_samplelimits = _dev_inst->get_sample_limit(); + _data_updated = false; + _view_timer.start(ViewTime); + + // Init and Set sample rate for all SignalData + // Logic/Analog/Dso + if (_logic_data) { + _logic_data->init(); + _logic_data->set_samplerate(_cur_samplerate); + } + if (_analog_data) { + _analog_data->init(); + _analog_data->set_samplerate(_cur_samplerate); + } + if (_dso_data) { + _dso_data->init(); + _dso_data->set_samplerate(_cur_samplerate); + } + // Group + if (_group_data) { + _group_data->init(); + _group_data->set_samplerate(_cur_samplerate); + } +#ifdef ENABLE_DECODE + // DecoderStack + BOOST_FOREACH(const boost::shared_ptr d, _decode_traces) + { + assert(d); + d->decoder()->init(); + d->decoder()->set_samplerate(_cur_samplerate); + } +#endif + // MathStack + BOOST_FOREACH(const boost::shared_ptr m, _math_traces) + { + assert(m); + m->get_math_stack()->init(); + m->get_math_stack()->set_samplerate(_cur_samplerate); + } +} + void SigSession::start_capture(bool instant, boost::function error_handler) { stop_capture(); + capture_init(); // Check that a device instance has been selected. if (!_dev_inst) { @@ -466,7 +524,8 @@ void SigSession::start_capture(bool instant, } if (!l) { error_handler(tr("No probes enabled.")); - capture_state_changed(_capture_state); + data_updated(); + capture_state_changed(SigSession::Stopped); return; } @@ -475,8 +534,6 @@ void SigSession::start_capture(bool instant, _instant = instant; else _instant = true; - if (~_instant) - _view_timer.blockSignals(false); // Begin the session _sampling_thread.reset(new boost::thread( @@ -487,6 +544,7 @@ void SigSession::start_capture(bool instant, void SigSession::stop_capture() { _instant = false; + _view_timer.stop(); #ifdef ENABLE_DECODE for (vector< boost::shared_ptr >::iterator i = _decode_traces.begin(); @@ -497,7 +555,6 @@ void SigSession::stop_capture() if (get_capture_state() != Running) return; sr_session_stop(); - _view_timer.blockSignals(true); // Check that sampling stopped if (_sampling_thread.get()) @@ -507,19 +564,19 @@ void SigSession::stop_capture() vector< boost::shared_ptr > SigSession::get_signals() { - boost::lock_guard lock(_signals_mutex); + //boost::lock_guard lock(_signals_mutex); return _signals; } vector< boost::shared_ptr > SigSession::get_group_signals() { - boost::lock_guard lock(_signals_mutex); + //boost::lock_guard lock(_signals_mutex); return _group_traces; } set< boost::shared_ptr > SigSession::get_data() const { - lock_guard lock(_signals_mutex); + //lock_guard lock(_signals_mutex); set< boost::shared_ptr > data; BOOST_FOREACH(const boost::shared_ptr sig, _signals) { assert(sig); @@ -577,7 +634,7 @@ const void* SigSession::get_buf(int& unit_size, uint64_t &length) void SigSession::set_capture_state(capture_state state) { - boost::lock_guard lock(_sampling_mutex); + boost::lock_guard lock(_sampling_mutex); _capture_state = state; data_updated(); capture_state_changed(state); @@ -601,65 +658,24 @@ void SigSession::sample_thread_proc(boost::shared_ptr dev_inst, set_capture_state(Running); dev_inst->run(); - set_capture_state(Stopped); // Confirm that SR_DF_END was received assert(_cur_logic_snapshot->last_ended()); assert(_cur_dso_snapshot->last_ended()); assert(_cur_analog_snapshot->last_ended()); + set_capture_state(Stopped); +} + +void SigSession::check_update() +{ + if (_data_updated) { + data_updated(); + _data_updated = false; + } } void SigSession::feed_in_header(const sr_dev_inst *sdi) { - GVariant *gvar; - int ret; - - // Read out the sample rate - if(sdi->driver) - { - ret = sr_config_get(sdi->driver, sdi, NULL, NULL, SR_CONF_SAMPLERATE, &gvar); - if (ret != SR_OK) { - hardware_connect_failed(); - return; - } - - _cur_samplerate = g_variant_get_uint64(gvar); - g_variant_unref(gvar); - - ret = sr_config_get(sdi->driver, sdi, NULL, NULL, SR_CONF_LIMIT_SAMPLES, &gvar); - if (ret != SR_OK) { - hardware_connect_failed(); - return; - } - - _cur_samplelimits = g_variant_get_uint64(gvar); - g_variant_unref(gvar); - } - - // Set the sample rate of all SignalData - // Logic/Analog/Dso - if (_logic_data) - _logic_data->set_samplerate(_cur_samplerate); - if (_analog_data) - _analog_data->set_samplerate(_cur_samplerate); - if (_dso_data) - _dso_data->set_samplerate(_cur_samplerate); -#ifdef ENABLE_DECODE - // DecoderStack - BOOST_FOREACH(const boost::shared_ptr d, _decode_traces) - { - assert(d); - d->decoder()->set_samplerate(_cur_samplerate); - } -#endif - // MathStack - BOOST_FOREACH(const boost::shared_ptr m, _math_traces) - { - assert(m); - m->get_math_stack()->set_samplerate(_cur_samplerate); - } - // Group - _group_data->set_samplerate(_cur_samplerate); } void SigSession::add_group() @@ -677,6 +693,7 @@ void SigSession::add_group() //_group_data.reset(new data::Group(_last_sample_rate)); // if (_group_data->get_snapshots().empty()) // _group_data->set_samplerate(_dev_inst->get_sample_rate()); + _group_data->init(); _group_data->set_samplerate(_cur_samplerate); const boost::shared_ptr signal( new view::GroupSignal("New Group", @@ -747,6 +764,15 @@ void SigSession::init_signals() unsigned int dso_probe_count = 0; unsigned int analog_probe_count = 0; + if (_logic_data) + _logic_data->clear(); + if (_dso_data) + _dso_data->clear(); + if (_analog_data) + _analog_data->clear(); + if (_group_data) + _group_data->clear(); + #ifdef ENABLE_DECODE // Clear the decode traces _decode_traces.clear(); @@ -777,51 +803,14 @@ void SigSession::init_signals() } } - // Create snapshots - { - _cur_logic_snapshot.reset(new data::LogicSnapshot()); - assert(_cur_logic_snapshot); - - _cur_dso_snapshot.reset(new data::DsoSnapshot()); - assert(_cur_dso_snapshot); - - _cur_analog_snapshot.reset(new data::AnalogSnapshot()); - assert(_cur_analog_snapshot); - } - - // Create data containers for the coming data snapshots - { - if (logic_probe_count != 0) { - _logic_data.reset(new data::Logic()); - assert(_logic_data); - _logic_data->push_snapshot(_cur_logic_snapshot); - } - - if (dso_probe_count != 0) { - _dso_data.reset(new data::Dso()); - assert(_dso_data); - _dso_data->push_snapshot(_cur_dso_snapshot); - } - - if (analog_probe_count != 0) { - _analog_data.reset(new data::Analog()); - assert(_analog_data); - _analog_data->push_snapshot(_cur_analog_snapshot); - } - - _group_data.reset(new data::Group()); - assert(_group_data); - _group_cnt = 0; - } - // Make the logic probe list { _group_traces.clear(); vector< boost::shared_ptr >().swap(_group_traces); - for (const GSList *l = _dev_inst->dev_inst()->channels; l; l = l->next) { - const sr_channel *const probe = - (const sr_channel *)l->data; + for (GSList *l = _dev_inst->dev_inst()->channels; l; l = l->next) { + sr_channel *probe = + ( sr_channel *)l->data; assert(probe); signal.reset(); switch(probe->type) { @@ -852,7 +841,7 @@ void SigSession::init_signals() } mathTraces_rebuild(); - data_updated(); + //data_updated(); } void SigSession::reload() @@ -868,9 +857,9 @@ void SigSession::reload() // Make the logic probe list { - for (const GSList *l = _dev_inst->dev_inst()->channels; l; l = l->next) { - const sr_channel *const probe = - (const sr_channel *)l->data; + for (GSList *l = _dev_inst->dev_inst()->channels; l; l = l->next) { + sr_channel *probe = + (sr_channel *)l->data; assert(probe); signal.reset(); switch(probe->type) { @@ -918,37 +907,38 @@ void SigSession::reload() void SigSession::refresh(int holdtime) { + boost::lock_guard lock(_data_mutex); + + if (strncmp(_dev_inst->dev_inst()->driver->name, "virtual", 7)) { + _data_lock = true; + _refresh_timer.start(holdtime); + } if (_logic_data) { - _logic_data->clear(); + _logic_data->init(); //_cur_logic_snapshot.reset(); #ifdef ENABLE_DECODE BOOST_FOREACH(const boost::shared_ptr d, _decode_traces) { assert(d); - d->decoder()->clear(); + d->decoder()->init(); } #endif } if (_dso_data) { - _dso_data->clear(); - //_cur_dso_snapshot.reset(); - _cur_dso_snapshot->set_last_ended(true); + _dso_data->init(); // MathStack BOOST_FOREACH(const boost::shared_ptr m, _math_traces) { assert(m); - m->get_math_stack()->clear(); + m->get_math_stack()->init(); } } if (_analog_data) { - _analog_data->clear(); + _analog_data->init(); //_cur_analog_snapshot.reset(); } - if (strncmp(_dev_inst->dev_inst()->driver->name, "virtual", 7)) { - _data_lock = true; - _refresh_timer.start(holdtime); - } - data_updated(); + //data_updated(); + _data_updated = true; } void SigSession::data_unlock() @@ -1002,9 +992,8 @@ void SigSession::feed_in_trigger(const ds_trigger_pos &trigger_pos) void SigSession::feed_in_logic(const sr_datafeed_logic &logic) { - boost::lock_guard lock(_data_mutex); - - if (!_logic_data || !_cur_logic_snapshot) { + //boost::lock_guard lock(_data_mutex); + if (!_logic_data || _cur_logic_snapshot->memory_failed()) { qDebug() << "Unexpected logic packet"; return; } @@ -1036,9 +1025,9 @@ void SigSession::feed_in_logic(const sr_datafeed_logic &logic) void SigSession::feed_in_dso(const sr_datafeed_dso &dso) { - boost::lock_guard lock(_data_mutex); + //boost::lock_guard lock(_data_mutex); - if(!_dso_data || !_cur_dso_snapshot) + if(!_dso_data || _cur_dso_snapshot->memory_failed()) { qDebug() << "Unexpected dso packet"; return; // This dso packet was not expected. @@ -1074,14 +1063,15 @@ void SigSession::feed_in_dso(const sr_datafeed_dso &dso) } receive_data(dso.num_samples); - data_updated(); + //data_updated(); + _data_updated = true; } void SigSession::feed_in_analog(const sr_datafeed_analog &analog) { - boost::lock_guard lock(_data_mutex); + //boost::lock_guard lock(_data_mutex); - if(!_analog_data || !_cur_analog_snapshot) + if(!_analog_data || _cur_analog_snapshot->memory_failed()) { qDebug() << "Unexpected analog packet"; return; // This analog packet was not expected. @@ -1101,7 +1091,8 @@ void SigSession::feed_in_analog(const sr_datafeed_analog &analog) } receive_data(analog.num_samples); - data_updated(); + //data_updated(); + _data_updated = true; } void SigSession::data_feed_in(const struct sr_dev_inst *sdi, @@ -1110,6 +1101,8 @@ void SigSession::data_feed_in(const struct sr_dev_inst *sdi, assert(sdi); assert(packet); + boost::lock_guard lock(_data_mutex); + if (_data_lock) return; @@ -1147,7 +1140,7 @@ void SigSession::data_feed_in(const struct sr_dev_inst *sdi, case SR_DF_END: { { - boost::lock_guard lock(_data_mutex); + //boost::lock_guard lock(_data_mutex); if (!_cur_logic_snapshot->empty()) { BOOST_FOREACH(const boost::shared_ptr g, _group_traces) { @@ -1167,7 +1160,6 @@ void SigSession::data_feed_in(const struct sr_dev_inst *sdi, d->frame_ended(); #endif } - frame_ended(); break; } @@ -1378,7 +1370,7 @@ bool SigSession::add_decoder(srd_decoder *const dec) vector< boost::shared_ptr > SigSession::get_decode_signals() const { - lock_guard lock(_signals_mutex); + //lock_guard lock(_signals_mutex); return _decode_traces; } @@ -1491,7 +1483,7 @@ void SigSession::mathTraces_rebuild() vector< boost::shared_ptr > SigSession::get_math_signals() { - lock_guard lock(_signals_mutex); + //lock_guard lock(_signals_mutex); return _math_traces; } diff --git a/DSView/pv/sigsession.h b/DSView/pv/sigsession.h index 95769fe6..8ff3e0d9 100644 --- a/DSView/pv/sigsession.h +++ b/DSView/pv/sigsession.h @@ -92,7 +92,7 @@ class SigSession : public QObject private: static constexpr float Oversampling = 2.0f; - static const int ViewTime = 800; + static const int ViewTime = 50; static const int RefreshTime = 500; bool saveFileThreadRunning = false; @@ -134,7 +134,7 @@ public: void start_capture(bool instant, boost::function error_handler); - + void capture_init(); void stop_capture(); std::set< boost::shared_ptr > get_data() const; @@ -236,13 +236,13 @@ private: */ boost::shared_ptr _dev_inst; - mutable boost::mutex _sampling_mutex; + mutable boost::mutex _sampling_mutex; capture_state _capture_state; bool _instant; uint64_t _cur_samplerate; uint64_t _cur_samplelimits; - mutable boost::mutex _signals_mutex; + //mutable boost::mutex _signals_mutex; std::vector< boost::shared_ptr > _signals; std::vector< boost::shared_ptr > _group_traces; #ifdef ENABLE_DECODE @@ -272,6 +272,7 @@ private: QTimer _view_timer; QTimer _refresh_timer; bool _data_lock; + bool _data_updated; signals: void capture_state_changed(int state); @@ -319,6 +320,7 @@ public slots: private slots: void cancelSaveFile(); void data_unlock(); + void check_update(); private: // TODO: This should not be necessary. Multiple concurrent diff --git a/DSView/pv/storesession.cpp b/DSView/pv/storesession.cpp index 14fe7cd1..baac25c4 100644 --- a/DSView/pv/storesession.cpp +++ b/DSView/pv/storesession.cpp @@ -60,13 +60,13 @@ StoreSession::~StoreSession() pair StoreSession::progress() const { - lock_guard lock(_mutex); + //lock_guard lock(_mutex); return make_pair(_units_stored, _unit_count); } const QString& StoreSession::error() const { - lock_guard lock(_mutex); + //lock_guard lock(_mutex); return _error; } @@ -163,7 +163,7 @@ void StoreSession::store_proc(shared_ptr snapshot) assert(unit_size != 0); { - lock_guard lock(_mutex); + //lock_guard lock(_mutex); _unit_count = snapshot->get_sample_count(); } @@ -188,7 +188,7 @@ void StoreSession::store_proc(shared_ptr snapshot) start_sample = end_sample; { - lock_guard lock(_mutex); + //lock_guard lock(_mutex); _units_stored = start_sample; } } diff --git a/DSView/pv/storesession.h b/DSView/pv/storesession.h index 63f8003e..0493ff84 100644 --- a/DSView/pv/storesession.h +++ b/DSView/pv/storesession.h @@ -72,7 +72,7 @@ private: boost::thread _thread; - mutable boost::mutex _mutex; + //mutable boost::mutex _mutex; uint64_t _units_stored; uint64_t _unit_count; QString _error; diff --git a/DSView/pv/toolbars/samplingbar.cpp b/DSView/pv/toolbars/samplingbar.cpp index fd3c3129..c3bfd339 100644 --- a/DSView/pv/toolbars/samplingbar.cpp +++ b/DSView/pv/toolbars/samplingbar.cpp @@ -470,12 +470,12 @@ void SamplingBar::commit_sample_rate() // Get last samplerate last_sample_rate = get_selected_device()->get_sample_rate(); - if (last_sample_rate != sample_rate) { + //if (last_sample_rate != sample_rate) { // Set the samplerate get_selected_device()->set_config(NULL, NULL, SR_CONF_SAMPLERATE, g_variant_new_uint64(sample_rate)); - } + //} _updating_sample_rate = false; } @@ -731,6 +731,8 @@ void SamplingBar::on_device_selected() if (_updating_device_selector) return; + _session.stop_capture(); + const shared_ptr dev_inst = get_selected_device(); if (!dev_inst) return; diff --git a/DSView/pv/toolbars/trigbar.cpp b/DSView/pv/toolbars/trigbar.cpp index 50207684..509b28fc 100644 --- a/DSView/pv/toolbars/trigbar.cpp +++ b/DSView/pv/toolbars/trigbar.cpp @@ -119,6 +119,11 @@ void TrigBar::trigger_clicked() on_trigger(_trig_button.isChecked()); } +void TrigBar::update_trig_btn(bool checked) +{ + _trig_button.setChecked(checked); +} + void TrigBar::measure_clicked() { on_measure(_measure_button.isChecked()); diff --git a/DSView/pv/toolbars/trigbar.h b/DSView/pv/toolbars/trigbar.h index 53faf1d3..6c2048b7 100644 --- a/DSView/pv/toolbars/trigbar.h +++ b/DSView/pv/toolbars/trigbar.h @@ -57,6 +57,8 @@ public slots: void measure_clicked(); void search_clicked(); + void update_trig_btn(bool checked); + void on_actionFft_triggered(); private: diff --git a/DSView/pv/view/analogsignal.cpp b/DSView/pv/view/analogsignal.cpp index d9726117..e026b33e 100644 --- a/DSView/pv/view/analogsignal.cpp +++ b/DSView/pv/view/analogsignal.cpp @@ -53,7 +53,7 @@ const float AnalogSignal::EnvelopeThreshold = 256.0f; AnalogSignal::AnalogSignal(boost::shared_ptr dev_inst, boost::shared_ptr data, - const sr_channel * const probe) : + sr_channel *probe) : Signal(dev_inst, probe), _data(data) { diff --git a/DSView/pv/view/analogsignal.h b/DSView/pv/view/analogsignal.h index b8810fdb..12523de7 100644 --- a/DSView/pv/view/analogsignal.h +++ b/DSView/pv/view/analogsignal.h @@ -48,7 +48,7 @@ private: public: AnalogSignal(boost::shared_ptr dev_inst, boost::shared_ptr data, - const sr_channel * const probe); + sr_channel *probe); virtual ~AnalogSignal(); diff --git a/DSView/pv/view/decodetrace.cpp b/DSView/pv/view/decodetrace.cpp index 8d20ddca..2cce5a7e 100644 --- a/DSView/pv/view/decodetrace.cpp +++ b/DSView/pv/view/decodetrace.cpp @@ -256,8 +256,13 @@ void DecodeTrace::paint_mid(QPainter &p, int left, int right) const uint64_t start_sample = (uint64_t)max((left + pixels_offset) * samples_per_pixel, 0.0); - const uint64_t end_sample = (uint64_t)max((right + pixels_offset) * + uint64_t end_sample = (uint64_t)max((right + pixels_offset) * samples_per_pixel, 0.0); + const uint64_t samples_decoded = _decoder_stack->samples_decoded(); + if (samples_decoded < start_sample) + return; + if (samples_decoded < end_sample) + end_sample = samples_decoded; const int annotation_height = _view->get_signalHeight(); @@ -281,18 +286,13 @@ void DecodeTrace::paint_mid(QPainter &p, int left, int right) BOOST_FOREACH(boost::shared_ptr dec, _decoder_stack->stack()) { if (dec->shown()) { - const std::map& rows(_decoder_stack->get_rows_gshow()); + const std::map rows = _decoder_stack->get_rows_gshow(); for (std::map::const_iterator i = rows.begin(); i != rows.end(); i++) { if ((*i).first.decoder() == dec->decoder() && _decoder_stack->has_annotations((*i).first)) { if ((*i).second) { const Row &row = (*i).first; - size_t base_colour = 0x13579BDF; - boost::hash_combine(base_colour, this); - boost::hash_combine(base_colour, row.decoder()); - boost::hash_combine(base_colour, row.row()); - base_colour >>= 16; const uint64_t min_annotation = _decoder_stack->get_min_annotation(row); @@ -310,10 +310,10 @@ void DecodeTrace::paint_mid(QPainter &p, int left, int right) draw_annotation(a, p, get_text_colour(), annotation_height, left, right, samples_per_pixel, pixels_offset, y, - base_colour, min_annWidth); + 0, min_annWidth); } } else if (max_annWidth != 0){ - draw_nodetail(p, annotation_height, left, right, y, base_colour); + draw_nodetail(p, annotation_height, left, right, y, 0); } if (max_annWidth != 0) { y += annotation_height; @@ -397,6 +397,9 @@ bool DecodeTrace::create_popup() } } + if (_popup_form) + QWidget().setLayout(_popup_form); + delete _popup; _popup = NULL; _popup_form = NULL; @@ -522,7 +525,7 @@ void DecodeTrace::draw_annotation(const pv::data::decode::Annotation &a, const double end = min(a.end_sample() / samples_per_pixel - pixels_offset, (double)right); - const size_t colour = (base_colour + a.format()) % countof(Colours); + const size_t colour = ((base_colour + a.type()) % MaxAnnType) % countof(Colours); const QColor &fill = Colours[colour]; const QColor &outline = OutlineColours[colour]; @@ -699,11 +702,11 @@ void DecodeTrace::create_decoder_form( assert(decoder); pv::widgets::DecoderGroupBox *const group = - new pv::widgets::DecoderGroupBox(decoder_stack, dec); + new pv::widgets::DecoderGroupBox(decoder_stack, dec, parent); connect(group, SIGNAL(del_stack(boost::shared_ptr&)), this, SLOT(on_del_stack(boost::shared_ptr&))); - QFormLayout *const decoder_form = new QFormLayout; + QFormLayout *const decoder_form = new QFormLayout(); group->add_layout(decoder_form); // Add the mandatory channels @@ -840,6 +843,8 @@ void DecodeTrace::on_new_decode_data() if (_view && _view->session().get_capture_state() == SigSession::Stopped) _view->data_updated(); + if (_totalHeight/_view->get_signalHeight() != rows_size()) + _view->signals_changed(); } int DecodeTrace::get_progress() const @@ -895,7 +900,7 @@ int DecodeTrace::rows_size() BOOST_FOREACH(boost::shared_ptr dec, _decoder_stack->stack()) { if (dec->shown()) { - const std::map& rows(_decoder_stack->get_rows_gshow()); + const std::map rows = _decoder_stack->get_rows_gshow(); for (std::map::const_iterator i = rows.begin(); i != rows.end(); i++) { if ((*i).first.decoder() == dec->decoder() && diff --git a/DSView/pv/view/decodetrace.h b/DSView/pv/view/decodetrace.h index 12269b8c..ff65a1a8 100644 --- a/DSView/pv/view/decodetrace.h +++ b/DSView/pv/view/decodetrace.h @@ -91,6 +91,7 @@ private: static const int DefaultFontSize = 8; static const int ControlRectWidth = 5; + static const int MaxAnnType = 100; static const QString RegionStart; static const QString RegionEnd; diff --git a/DSView/pv/view/devmode.cpp b/DSView/pv/view/devmode.cpp index b782a11c..b9283f2b 100644 --- a/DSView/pv/view/devmode.cpp +++ b/DSView/pv/view/devmode.cpp @@ -115,8 +115,8 @@ void DevMode::on_mode_change() dev_inst->set_config(NULL, NULL, SR_CONF_DEVICE_MODE, g_variant_new_int16((*i).second->mode)); - button->setChecked(true); mode_changed(); + button->setChecked(true); } } else { (*i).first->setChecked(false); diff --git a/DSView/pv/view/dsosignal.cpp b/DSView/pv/view/dsosignal.cpp index 7ee1befd..24916a87 100644 --- a/DSView/pv/view/dsosignal.cpp +++ b/DSView/pv/view/dsosignal.cpp @@ -112,7 +112,7 @@ const int DsoSignal::RightMargin = 30; DsoSignal::DsoSignal(boost::shared_ptr dev_inst, boost::shared_ptr data, - const sr_channel * const probe): + sr_channel *probe): Signal(dev_inst, probe), _data(data), _scale(0), diff --git a/DSView/pv/view/dsosignal.h b/DSView/pv/view/dsosignal.h index 67c67da7..69ebcd4b 100644 --- a/DSView/pv/view/dsosignal.h +++ b/DSView/pv/view/dsosignal.h @@ -100,7 +100,7 @@ private: public: DsoSignal(boost::shared_ptr dev_inst, boost::shared_ptr data, - const sr_channel * const probe); + sr_channel *probe); virtual ~DsoSignal(); diff --git a/DSView/pv/view/logicsignal.cpp b/DSView/pv/view/logicsignal.cpp index cff3801c..e5d543b8 100644 --- a/DSView/pv/view/logicsignal.cpp +++ b/DSView/pv/view/logicsignal.cpp @@ -53,7 +53,7 @@ const int LogicSignal::StateRound = 5; LogicSignal::LogicSignal(boost::shared_ptr dev_inst, boost::shared_ptr data, - const sr_channel * const probe) : + sr_channel *probe) : Signal(dev_inst, probe), _data(data), _trig(NONTRIG) @@ -64,7 +64,7 @@ LogicSignal::LogicSignal(boost::shared_ptr dev_inst, LogicSignal::LogicSignal(boost::shared_ptr s, boost::shared_ptr data, - const sr_channel * const probe) : + sr_channel *probe) : Signal(*s.get(), probe), _data(data), _trig(s->get_trig()) diff --git a/DSView/pv/view/logicsignal.h b/DSView/pv/view/logicsignal.h index ac64b1b8..32f00683 100644 --- a/DSView/pv/view/logicsignal.h +++ b/DSView/pv/view/logicsignal.h @@ -65,11 +65,11 @@ private: public: LogicSignal(boost::shared_ptr dev_inst, boost::shared_ptr data, - const sr_channel * const probe); + sr_channel *probe); LogicSignal(boost::shared_ptr s, boost::shared_ptr data, - const sr_channel * const probe); + sr_channel *probe); virtual ~LogicSignal(); diff --git a/DSView/pv/view/mathtrace.cpp b/DSView/pv/view/mathtrace.cpp index 0abafb18..b19ea97e 100644 --- a/DSView/pv/view/mathtrace.cpp +++ b/DSView/pv/view/mathtrace.cpp @@ -359,8 +359,8 @@ void MathTrace::paint_fore(QPainter &p, int left, int right) double blank_right = width; // horizontal ruler - const double NyFreq = _session.cur_samplerate() / (2.0 * _math_stack->get_sample_interval()); - const double deltaFreq = _session.cur_samplerate() * 1.0 / + const double NyFreq = _session.get_device()->get_sample_rate() / (2.0 * _math_stack->get_sample_interval()); + const double deltaFreq = _session.get_device()->get_sample_rate() * 1.0 / (_math_stack->get_sample_num() * _math_stack->get_sample_interval()); const double FreqRange = NyFreq * _scale; const double FreqOffset = NyFreq * _offset; diff --git a/DSView/pv/view/signal.cpp b/DSView/pv/view/signal.cpp index 9abd0ce9..40c66101 100644 --- a/DSView/pv/view/signal.cpp +++ b/DSView/pv/view/signal.cpp @@ -35,14 +35,14 @@ namespace pv { namespace view { Signal::Signal(boost::shared_ptr dev_inst, - const sr_channel *const probe) : + sr_channel *probe) : Trace(probe->name, probe->index, probe->type), _dev_inst(dev_inst), _probe(probe) { } -Signal::Signal(const Signal &s, const sr_channel * const probe) : +Signal::Signal(const Signal &s, sr_channel *probe) : Trace((const Trace &)s), _dev_inst(s._dev_inst), _probe(probe) @@ -54,6 +54,13 @@ bool Signal::enabled() const return _probe->enabled; } +void Signal::set_name(QString name) +{ + Trace::set_name(name); + g_free(_probe->name); + _probe->name = g_strdup(name.toLocal8Bit().data()); +} + void Signal::paint_axis(QPainter &p, int y, int left, int right) { p.setPen(SignalAxisPen); diff --git a/DSView/pv/view/signal.h b/DSView/pv/view/signal.h index 01fabb25..4a18ad24 100644 --- a/DSView/pv/view/signal.h +++ b/DSView/pv/view/signal.h @@ -57,12 +57,12 @@ private: protected: Signal(boost::shared_ptr dev_inst, - const sr_channel * const probe); + sr_channel * const probe); /** * Copy constructor */ - Signal(const Signal &s, const sr_channel * const probe); + Signal(const Signal &s, sr_channel * const probe); public: virtual boost::shared_ptr data() const = 0; @@ -74,6 +74,11 @@ public: */ bool enabled() const; + /** + * Sets the name of the signal. + */ + void set_name(QString name); + /** * Paints the signal label into a QGLWidget. * @param p the QPainter to paint into. @@ -97,7 +102,7 @@ protected: protected: boost::shared_ptr _dev_inst; - const sr_channel *const _probe; + sr_channel *const _probe; }; } // namespace view diff --git a/DSView/pv/view/view.cpp b/DSView/pv/view/view.cpp index c3e2193d..a85eb321 100644 --- a/DSView/pv/view/view.cpp +++ b/DSView/pv/view/view.cpp @@ -149,7 +149,7 @@ View::View(SigSession &session, pv::toolbars::SamplingBar *sampling_bar, QWidget connect(&_session, SIGNAL(device_setted()), _devmode, SLOT(set_device())); connect(&_session, SIGNAL(signals_changed()), - this, SLOT(signals_changed())); + this, SLOT(signals_changed()), Qt::DirectConnection); connect(&_session, SIGNAL(data_updated()), this, SLOT(data_updated())); connect(&_session, SIGNAL(receive_trigger(quint64)), @@ -157,8 +157,10 @@ View::View(SigSession &session, pv::toolbars::SamplingBar *sampling_bar, QWidget connect(&_session, SIGNAL(show_region(uint64_t,uint64_t)), this, SLOT(show_region(uint64_t, uint64_t))); +// connect(_devmode, SIGNAL(mode_changed()), +// this, SIGNAL(mode_changed())); connect(_devmode, SIGNAL(mode_changed()), - this, SIGNAL(mode_changed())); + parent, SLOT(update_device_list()), Qt::DirectConnection); connect(_header, SIGNAL(traces_moved()), this, SLOT(on_traces_moved())); @@ -431,7 +433,7 @@ void View::set_trig_pos(quint64 trig_pos) } _trigger_time = QDateTime::currentDateTime(); - const int64_t secs = time - _session.cur_sampletime(); + const int64_t secs = time - _session.get_device()->get_sample_time(); _trigger_time = _trigger_time.addSecs(secs); _ruler->update(); @@ -498,7 +500,7 @@ void View::get_scroll_layout(double &length, double &offset) const if (data_set.empty()) return; - length = _session.cur_sampletime() / _scale; + length = _session.get_device()->get_sample_time() / _scale; offset = _offset / _scale; } @@ -645,6 +647,7 @@ void View::signals_changed() header_updated(); normalize_layout(); + update_scale_offset(); data_updated(); } @@ -972,7 +975,7 @@ double View::get_min_offset() double View::get_max_offset() { - return _session.cur_sampletime() + return _session.get_device()->get_sample_time() - _scale * (get_view_width() * MaxViewRate); } diff --git a/DSView/pv/view/viewport.cpp b/DSView/pv/view/viewport.cpp index 07f0dee8..199d35cb 100644 --- a/DSView/pv/view/viewport.cpp +++ b/DSView/pv/view/viewport.cpp @@ -179,6 +179,8 @@ void Viewport::paintEvent(QPaintEvent *event) void Viewport::paintSignals(QPainter &p) { + if (_view.session().get_data_lock()) + return; const vector< boost::shared_ptr > traces(_view.get_traces(_type)); if (_view.scale() != _curScale || _view.offset() != _curOffset || @@ -791,6 +793,8 @@ void Viewport::clear_measure() void Viewport::measure() { + if (_view.session().get_data_lock()) + return; _measure_type = NO_MEASURE; if (_type == TIME_VIEW) { const uint64_t sample_rate = _view.session().cur_samplerate(); diff --git a/DSView/pv/widgets/decodergroupbox.cpp b/DSView/pv/widgets/decodergroupbox.cpp index 4be51822..ab028284 100644 --- a/DSView/pv/widgets/decodergroupbox.cpp +++ b/DSView/pv/widgets/decodergroupbox.cpp @@ -46,12 +46,12 @@ DecoderGroupBox::DecoderGroupBox(boost::shared_ptr &decoder_ QWidget(parent), _decoder_stack(decoder_stack), _dec(dec), - _layout(new QGridLayout) + _layout(new QGridLayout(this)) { _layout->setContentsMargins(0, 0, 0, 0); setLayout(_layout); - _layout->addWidget(new QLabel(QString("

%1

").arg(_dec->decoder()->name)), + _layout->addWidget(new QLabel(QString("

%1

").arg(_dec->decoder()->name), this), 0, 0); _layout->setColumnStretch(0, 1); @@ -82,7 +82,7 @@ DecoderGroupBox::DecoderGroupBox(boost::shared_ptr &decoder_ // add row show/hide int index = 0; - const std::map& rows(_decoder_stack->get_rows_gshow()); + const std::map rows = _decoder_stack->get_rows_gshow(); for (std::map::const_iterator i = rows.begin(); i != rows.end(); i++) { if ((*i).first.decoder() == _dec->decoder()) { @@ -99,6 +99,10 @@ DecoderGroupBox::DecoderGroupBox(boost::shared_ptr &decoder_ } } +DecoderGroupBox::~DecoderGroupBox() +{ +} + void DecoderGroupBox::add_layout(QLayout *layout) { assert(layout); @@ -122,14 +126,14 @@ void DecoderGroupBox::tog_icon() } } } else { - std::map& rows(_decoder_stack->get_rows_gshow()); + std::map rows = _decoder_stack->get_rows_gshow(); for (std::map::const_iterator i = rows.begin(); i != rows.end(); i++) { if (index-- == 0) { _decoder_stack->set_rows_gshow((*i).first, !(*i).second); //rows[(*i).first] = !(*i).second; - sc->setIcon(QIcon(rows[(*i).first] ? ":/icons/shown.png" : - ":/icons/hidden.png")); + sc->setIcon(QIcon(rows[(*i).first] ? ":/icons/hidden.png" : + ":/icons/shown.png")); break; } } diff --git a/DSView/pv/widgets/decodergroupbox.h b/DSView/pv/widgets/decodergroupbox.h index d780241b..b83eef1c 100644 --- a/DSView/pv/widgets/decodergroupbox.h +++ b/DSView/pv/widgets/decodergroupbox.h @@ -46,7 +46,7 @@ public: DecoderGroupBox(boost::shared_ptr &decoder_stack, boost::shared_ptr &dec, QWidget *parent = NULL); - + ~DecoderGroupBox(); void add_layout(QLayout *layout); signals: diff --git a/libsigrok4DSL/hardware/DSL/dscope.c b/libsigrok4DSL/hardware/DSL/dscope.c index ef4f8dd8..30c0549d 100644 --- a/libsigrok4DSL/hardware/DSL/dscope.c +++ b/libsigrok4DSL/hardware/DSL/dscope.c @@ -436,6 +436,7 @@ static int fpga_config(struct libusb_device_handle *hdl, const char *filename) offset += chunksize; } fclose(fw); + g_free(buf); if (result == SR_OK) sr_info("FPGA configure done"); @@ -861,6 +862,7 @@ static GSList *scan(GSList *options) else sr_err("Firmware upload failed for " "device %d.", devcnt); + g_free(firmware); sdi->inst_type = SR_INST_USB; sdi->conn = sr_usb_dev_inst_new (libusb_get_bus_number(devlist[i]), 0xff, NULL); @@ -2109,6 +2111,7 @@ static int dev_open(struct sr_dev_inst *sdi) if (ret != SR_OK) { sr_err("Configure FPGA failed!"); } + g_free(fpga_bit); } if (sdi->mode == DSO) diff --git a/libsigrok4DSL/hardware/DSL/dsl.h b/libsigrok4DSL/hardware/DSL/dsl.h index 1b3f3c4e..6fd59f00 100644 --- a/libsigrok4DSL/hardware/DSL/dsl.h +++ b/libsigrok4DSL/hardware/DSL/dsl.h @@ -233,6 +233,7 @@ struct DSL_context { gboolean data_lock; int num_samples; + uint64_t sent_samples; int submitted_transfers; int empty_transfer_count; diff --git a/libsigrok4DSL/hardware/DSL/dslogic.c b/libsigrok4DSL/hardware/DSL/dslogic.c index b08feda5..e99f92ce 100644 --- a/libsigrok4DSL/hardware/DSL/dslogic.c +++ b/libsigrok4DSL/hardware/DSL/dslogic.c @@ -1013,142 +1013,6 @@ static uint64_t dso_cmd_gen(struct sr_dev_inst *sdi, struct sr_channel* ch, int return cmd; } -static int dev_open(struct sr_dev_inst *sdi) -{ - struct sr_usb_dev_inst *usb; - struct DSL_context *devc; - GSList *l; - int ret; - int64_t timediff_us, timediff_ms; - - devc = sdi->priv; - usb = sdi->conn; - - /* - * If the firmware was recently uploaded, wait up to MAX_RENUM_DELAY_MS - * milliseconds for the FX2 to renumerate. - */ - ret = SR_ERR; - if (devc->fw_updated > 0) { - sr_info("Waiting for device to reset."); - /* Takes >= 300ms for the FX2 to be gone from the USB bus. */ - g_usleep(300 * 1000); - timediff_ms = 0; - while (timediff_ms < MAX_RENUM_DELAY_MS) { - if ((ret = DSLogic_dev_open(sdi)) == SR_OK) - break; - g_usleep(100 * 1000); - - timediff_us = g_get_monotonic_time() - devc->fw_updated; - timediff_ms = timediff_us / 1000; - sr_spew("Waited %" PRIi64 "ms.", timediff_ms); - } - if (ret != SR_OK) { - sr_err("Device failed to renumerate."); - return SR_ERR; - } - sr_info("Device came back after %" PRIi64 "ms.", timediff_ms); - } else { - sr_info("Firmware upload was not needed."); - ret = DSLogic_dev_open(sdi); - } - - if (ret != SR_OK) { - sr_err("Unable to open device."); - return SR_ERR; - } - - ret = libusb_claim_interface(usb->devhdl, USB_INTERFACE); - if (ret != 0) { - switch(ret) { - case LIBUSB_ERROR_BUSY: - sr_err("Unable to claim USB interface. Another " - "program or driver has already claimed it."); - break; - case LIBUSB_ERROR_NO_DEVICE: - sr_err("Device has been disconnected."); - break; - default: - sr_err("Unable to claim interface: %s.", - libusb_error_name(ret)); - break; - } - - return SR_ERR; - } - - if ((ret = command_fpga_config(usb->devhdl)) != SR_OK) { - sr_err("Send FPGA configure command failed!"); - } else { - /* Takes >= 10ms for the FX2 to be ready for FPGA configure. */ - g_usleep(10 * 1000); - char *fpga_bit; - if (!(fpga_bit = g_try_malloc(strlen(config_path)+strlen(devc->profile->fpga_bit33)+1))) { - sr_err("fpag_bit path malloc error!"); - return SR_ERR_MALLOC; - } - strcpy(fpga_bit, config_path); - switch(devc->th_level) { - case SR_TH_3V3: - strcat(fpga_bit, devc->profile->fpga_bit33);; - break; - case SR_TH_5V0: - strcat(fpga_bit, devc->profile->fpga_bit50);; - break; - default: - return SR_ERR; - } - ret = fpga_config(usb->devhdl, fpga_bit); - if (ret != SR_OK) { - sr_err("Configure FPGA failed!"); - } - g_free(fpga_bit); - } - - ret = command_wr_reg(usb->devhdl, (uint8_t)(devc->vth/5.0*255), VTH_ADDR); - if (ret == SR_OK) - sr_dbg("%s: setting threshold voltage to %d", - __func__, devc->vth); - else - sr_dbg("%s: setting threshold voltage to %d failed", - __func__, devc->vth); - - return SR_OK; -} - -static int dev_close(struct sr_dev_inst *sdi) -{ - struct sr_usb_dev_inst *usb; - - usb = sdi->conn; - if (usb->devhdl == NULL) - return SR_ERR; - - sr_info("DSLogic: Closing device %d on %d.%d interface %d.", - sdi->index, usb->bus, usb->address, USB_INTERFACE); - libusb_release_interface(usb->devhdl, USB_INTERFACE); - libusb_close(usb->devhdl); - usb->devhdl = NULL; - sdi->status = SR_ST_INACTIVE; - - return SR_OK; -} - -static int cleanup(void) -{ - int ret; - struct drv_context *drvc; - - if (!(drvc = di->priv)) - return SR_OK; - - ret = dev_clear(); - - g_free(drvc); - di->priv = NULL; - - return ret; -} static int config_get(int id, GVariant **data, const struct sr_dev_inst *sdi, const struct sr_channel *ch, @@ -1904,13 +1768,176 @@ static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi, case SR_CONF_MAX_HEIGHT: *data = g_variant_new_strv(maxHeights, ARRAY_SIZE(maxHeights)); break; - default: + default: return SR_ERR_NA; - } + } return SR_OK; } +static int dev_open(struct sr_dev_inst *sdi) +{ + struct sr_usb_dev_inst *usb; + struct DSL_context *devc; + GSList *l; + int ret; + int64_t timediff_us, timediff_ms; + + devc = sdi->priv; + usb = sdi->conn; + + /* + * If the firmware was recently uploaded, wait up to MAX_RENUM_DELAY_MS + * milliseconds for the FX2 to renumerate. + */ + ret = SR_ERR; + if (devc->fw_updated > 0) { + sr_info("Waiting for device to reset."); + /* Takes >= 300ms for the FX2 to be gone from the USB bus. */ + g_usleep(300 * 1000); + timediff_ms = 0; + while (timediff_ms < MAX_RENUM_DELAY_MS) { + if ((ret = DSLogic_dev_open(sdi)) == SR_OK) + break; + g_usleep(100 * 1000); + + timediff_us = g_get_monotonic_time() - devc->fw_updated; + timediff_ms = timediff_us / 1000; + sr_spew("Waited %" PRIi64 "ms.", timediff_ms); + } + if (ret != SR_OK) { + sr_err("Device failed to renumerate."); + return SR_ERR; + } + sr_info("Device came back after %" PRIi64 "ms.", timediff_ms); + } else { + sr_info("Firmware upload was not needed."); + ret = DSLogic_dev_open(sdi); + } + + if (ret != SR_OK) { + sr_err("Unable to open device."); + return SR_ERR; + } + + ret = libusb_claim_interface(usb->devhdl, USB_INTERFACE); + if (ret != 0) { + switch(ret) { + case LIBUSB_ERROR_BUSY: + sr_err("Unable to claim USB interface. Another " + "program or driver has already claimed it."); + break; + case LIBUSB_ERROR_NO_DEVICE: + sr_err("Device has been disconnected."); + break; + default: + sr_err("Unable to claim interface: %s.", + libusb_error_name(ret)); + break; + } + + return SR_ERR; + } + + if (devc->fw_updated > 0) { + if ((ret = command_fpga_config(usb->devhdl)) != SR_OK) { + sr_err("Send FPGA configure command failed!"); + } else { + /* Takes >= 10ms for the FX2 to be ready for FPGA configure. */ + g_usleep(10 * 1000); + char *fpga_bit; + if (!(fpga_bit = g_try_malloc(strlen(config_path)+strlen(devc->profile->fpga_bit33)+1))) { + sr_err("fpag_bit path malloc error!"); + return SR_ERR_MALLOC; + } + strcpy(fpga_bit, config_path); + switch(devc->th_level) { + case SR_TH_3V3: + strcat(fpga_bit, devc->profile->fpga_bit33);; + break; + case SR_TH_5V0: + strcat(fpga_bit, devc->profile->fpga_bit50);; + break; + default: + return SR_ERR; + } + ret = fpga_config(usb->devhdl, fpga_bit); + if (ret != SR_OK) { + sr_err("Configure FPGA failed!"); + } + g_free(fpga_bit); + } + } + + ret = command_wr_reg(usb->devhdl, (uint8_t)(devc->vth/5.0*255), VTH_ADDR); + if (ret == SR_OK) + sr_dbg("%s: setting threshold voltage to %d", + __func__, devc->vth); + else + sr_dbg("%s: setting threshold voltage to %d failed", + __func__, devc->vth); + + #ifdef _WIN32 + if (pipe(devc->pipe_fds)) { + /* TODO: Better error message. */ + sr_err("%s: pipe() failed", __func__); + return SR_ERR; + } + devc->channel = g_io_channel_unix_new(devc->pipe_fds[0]); + g_io_channel_set_flags(devc->channel, G_IO_FLAG_NONBLOCK, NULL); + /* Set channel encoding to binary (default is UTF-8). */ + g_io_channel_set_encoding(devc->channel, NULL, NULL); + /* Make channels to unbuffered. */ + g_io_channel_set_buffered(devc->channel, FALSE); + #endif + + return SR_OK; +} + +static int dev_close(struct sr_dev_inst *sdi) +{ + struct sr_usb_dev_inst *usb; + struct DSL_context *devc; + + usb = sdi->conn; + if (usb->devhdl == NULL) + return SR_ERR; + devc = sdi->priv; + + #ifdef _WIN32 + if (sdi->status == SR_ST_ACTIVE && devc->channel) { + g_io_channel_shutdown(devc->channel, FALSE, NULL); + g_io_channel_unref(devc->channel); + devc->channel = NULL; + } + #endif + + sr_info("DSLogic: Closing device %d on %d.%d interface %d.", + sdi->index, usb->bus, usb->address, USB_INTERFACE); + libusb_release_interface(usb->devhdl, USB_INTERFACE); + libusb_close(usb->devhdl); + usb->devhdl = NULL; + sdi->status = SR_ST_INACTIVE; + + return SR_OK; +} + +static int cleanup(void) +{ + int ret; + struct drv_context *drvc; + + if (!(drvc = di->priv)) + return SR_OK; + + ret = dev_clear(); + + g_free(drvc); + di->priv = NULL; + + return ret; +} + static void abort_acquisition(struct DSL_context *devc) { int i; @@ -2246,12 +2273,13 @@ static void receive_transfer(struct libusb_transfer *transfer) /* send data to session bus */ sr_session_send(devc->cb_data, &packet); + devc->sent_samples += cur_sample_count; } devc->num_samples += cur_sample_count; - if (((*(struct sr_dev_inst *)(devc->cb_data)).mode == LOGIC || devc->instant) && - devc->limit_samples && - (unsigned int)devc->num_samples >= devc->actual_samples) { + if (((*(struct sr_dev_inst *)(devc->cb_data)).mode == LOGIC || devc->instant) && + devc->limit_samples && + (unsigned int)devc->num_samples >= devc->actual_samples) { //abort_acquisition(devc); free_transfer(transfer); devc->status = DSL_STOP; @@ -2391,6 +2419,8 @@ static int receive_data(int fd, int revents, const struct sr_dev_inst *sdi) if (devc->num_samples != -1 && (devc->status == DSL_STOP || devc->status == DSL_ERROR)) { + if (devc->sent_samples < devc->actual_samples) + devc->sent_samples = 0; sr_info("%s: Stopping", __func__); abort_acquisition(devc); } @@ -2503,6 +2533,7 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data) //devc->cb_data = cb_data; devc->cb_data = sdi; devc->num_samples = 0; + devc->sent_samples = 0; devc->empty_transfer_count = 0; devc->status = DSL_INIT; devc->num_transfers = 0; diff --git a/libsigrok4DSL/hardware/demo/demo.c b/libsigrok4DSL/hardware/demo/demo.c index e9536f4d..e26036c6 100644 --- a/libsigrok4DSL/hardware/demo/demo.c +++ b/libsigrok4DSL/hardware/demo/demo.c @@ -48,7 +48,8 @@ /* The size of chunks to send through the session bus. */ /* TODO: Should be configurable. */ -#define BUFSIZE 1024*1024 +#define BUFSIZE 512*1024 +#define DSO_BUFSIZE 10*1024 #define PERIOD 4000 @@ -319,7 +320,7 @@ static GSList *hw_scan(GSList *options) devices = NULL; - sdi = sr_dev_inst_new(LOGIC, 0, SR_ST_ACTIVE, DEMONAME, NULL, NULL); + sdi = sr_dev_inst_new(LOGIC, 0, SR_ST_INITIALIZING, DEMONAME, NULL, NULL); if (!sdi) { sr_err("Device instance creation failed."); return NULL; @@ -391,20 +392,39 @@ static GSList *hw_dev_mode_list(const struct sr_dev_inst *sdi) static int hw_dev_open(struct sr_dev_inst *sdi) { - (void)sdi; + //(void)sdi; + struct dev_context *const devc = sdi->priv; - sdi->status = SR_ST_ACTIVE; + sdi->status = SR_ST_ACTIVE; + + if (pipe(devc->pipe_fds)) { + /* TODO: Better error message. */ + sr_err("%s: pipe() failed", __func__); + return SR_ERR; + } + devc->channel = g_io_channel_unix_new(devc->pipe_fds[0]); + g_io_channel_set_flags(devc->channel, G_IO_FLAG_NONBLOCK, NULL); + /* Set channel encoding to binary (default is UTF-8). */ + g_io_channel_set_encoding(devc->channel, NULL, NULL); + /* Make channels to unbuffered. */ + g_io_channel_set_buffered(devc->channel, FALSE); return SR_OK; } static int hw_dev_close(struct sr_dev_inst *sdi) { - (void)sdi; + //(void)sdi; + struct dev_context *const devc = sdi->priv; + if (sdi->status == SR_ST_ACTIVE && devc->channel) { + g_io_channel_shutdown(devc->channel, FALSE, NULL); + g_io_channel_unref(devc->channel); + devc->channel = NULL; + } sdi->status = SR_ST_INACTIVE; - return SR_OK; + return SR_OK; } static int hw_cleanup(void) @@ -484,6 +504,9 @@ static int config_get(int id, GVariant **data, const struct sr_dev_inst *sdi, case SR_CONF_COUPLING: *data = g_variant_new_byte(ch->coupling); break; + case SR_CONF_TRIGGER_VALUE: + *data = g_variant_new_byte(ch->trig_value); + break; case SR_CONF_EN_CH: *data = g_variant_new_uint64(ch->enabled); break; @@ -667,6 +690,11 @@ static int config_set(int id, GVariant *data, struct sr_dev_inst *sdi, sr_dbg("%s: setting AC COUPLING of channel %d to %d", __func__, ch->index, ch->coupling); ret = SR_OK; + } else if (id == SR_CONF_TRIGGER_VALUE) { + ch->trig_value = g_variant_get_byte(data); + sr_dbg("%s: setting channel %d Trigger Value to %d", + __func__, ch->index, ch->trig_value); + ret = SR_OK; } else { ret = SR_ERR_NA; } @@ -843,7 +871,7 @@ static int receive_data(int fd, int revents, const struct sr_dev_inst *sdi) struct sr_datafeed_logic logic; struct sr_datafeed_dso dso; struct sr_datafeed_analog analog; - static uint64_t samples_to_send, expected_samplenum, sending_now; + static uint64_t samples_to_send = 0, expected_samplenum, sending_now; int64_t time, elapsed; static uint16_t last_sample = 0; uint16_t cur_sample; @@ -860,19 +888,22 @@ static int receive_data(int fd, int revents, const struct sr_dev_inst *sdi) /* Of those, how many do we still have to send? */ //samples_to_send = (expected_samplenum - devc->samples_counter) / CONST_LEN * CONST_LEN; //samples_to_send = expected_samplenum / CONST_LEN * CONST_LEN; - samples_to_send = ceil(elapsed / 1000000.0 * devc->cur_samplerate); + samples_to_send += ceil(elapsed / 1000000.0 * devc->cur_samplerate); if (devc->limit_samples) { - if ((sdi->mode == DSO && !devc->instant) || sdi->mode == ANALOG) + if (sdi->mode == DSO && !devc->instant) samples_to_send = MIN(samples_to_send, devc->limit_samples - devc->pre_index); + else if (sdi->mode == ANALOG) + samples_to_send = MIN(samples_to_send * g_slist_length(sdi->channels), + devc->limit_samples - devc->pre_index); else samples_to_send = MIN(samples_to_send, devc->limit_samples - devc->samples_counter); } - while (samples_to_send > 0) { - sending_now = MIN(samples_to_send, BUFSIZE); + if (samples_to_send > 0 && !devc->stop) { + sending_now = MIN(samples_to_send, (sdi->mode == DSO ) ? DSO_BUFSIZE : BUFSIZE); samples_generator(devc->buf, sending_now, sdi, devc); if (devc->trigger_stage != 0) { @@ -959,8 +990,6 @@ static int receive_data(int fd, int revents, const struct sr_dev_inst *sdi) devc->mstatus.captured_cnt1 = devc->samples_counter >> 8; devc->mstatus.captured_cnt2 = devc->samples_counter >> 16; devc->mstatus.captured_cnt3 = devc->samples_counter >> 32; - } else { - break; } } @@ -1015,30 +1044,15 @@ static int hw_dev_acquisition_start(const struct sr_dev_inst *sdi, * They are kept here because it provides a convenient way of setting * up a timeout-based polling mechanism. */ - if (pipe(devc->pipe_fds)) { - /* TODO: Better error message. */ - sr_err("%s: pipe() failed", __func__); - return SR_ERR; - } - - devc->channel = g_io_channel_unix_new(devc->pipe_fds[0]); - - g_io_channel_set_flags(devc->channel, G_IO_FLAG_NONBLOCK, NULL); - - /* Set channel encoding to binary (default is UTF-8). */ - g_io_channel_set_encoding(devc->channel, NULL, NULL); - - /* Make channels to unbuffered. */ - g_io_channel_set_buffered(devc->channel, FALSE); sr_session_source_add_channel(devc->channel, G_IO_IN | G_IO_ERR, - 100, receive_data, sdi); + (sdi->mode == DSO) ? 50 : 10, receive_data, sdi); /* Send header packet to the session bus. */ //std_session_send_df_header(cb_data, LOG_PREFIX); std_session_send_df_header(sdi, LOG_PREFIX); - if (!(devc->buf = g_try_malloc(BUFSIZE*sizeof(uint16_t)))) { + if (!(devc->buf = g_try_malloc(((sdi->mode == DSO ) ? DSO_BUFSIZE : BUFSIZE)*sizeof(uint16_t)))) { sr_err("buf for receive_data malloc failed."); return FALSE; } @@ -1051,18 +1065,18 @@ static int hw_dev_acquisition_start(const struct sr_dev_inst *sdi, static int hw_dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data) { - struct dev_context *const devc = sdi->priv; - struct sr_datafeed_packet packet; + (void)cb_data; - (void)cb_data; + struct dev_context *const devc = sdi->priv; + struct sr_datafeed_packet packet; + if (devc->stop) + return SR_OK; sr_dbg("Stopping aquisition."); devc->stop = TRUE; + sr_session_source_remove_channel(devc->channel); - g_io_channel_shutdown(devc->channel, FALSE, NULL); - g_io_channel_unref(devc->channel); - devc->channel = NULL; g_free(devc->buf); diff --git a/libsigrok4DSL/libsigrok.h b/libsigrok4DSL/libsigrok.h index 12cf5eaa..319a35f5 100644 --- a/libsigrok4DSL/libsigrok.h +++ b/libsigrok4DSL/libsigrok.h @@ -1046,7 +1046,7 @@ struct sr_session { * an async fashion. We need to make sure the session is stopped from * within the session thread itself. */ -// GMutex stop_mutex; + GMutex stop_mutex; gboolean abort_session; }; diff --git a/libsigrok4DSL/session.c b/libsigrok4DSL/session.c index 7fb110d9..87746adb 100644 --- a/libsigrok4DSL/session.c +++ b/libsigrok4DSL/session.c @@ -86,7 +86,7 @@ SR_API struct sr_session *sr_session_new(void) session->source_timeout = -1; session->running = FALSE; session->abort_session = FALSE; -// g_mutex_init(&session->stop_mutex); + g_mutex_init(&session->stop_mutex); return session; } @@ -121,7 +121,7 @@ SR_API int sr_session_destroy(void) /* TODO: Error checks needed? */ -// g_mutex_clear(&session->stop_mutex); + g_mutex_clear(&session->stop_mutex); g_free(session); session = NULL; @@ -325,13 +325,13 @@ static int sr_session_iteration(gboolean block) * we check the flag after processing every source, not * just once per main event loop. */ - //g_mutex_lock(&session->stop_mutex); + g_mutex_lock(&session->stop_mutex); if (session->abort_session) { sr_session_stop_sync(); /* But once is enough. */ session->abort_session = FALSE; } - //g_mutex_unlock(&session->stop_mutex); + g_mutex_unlock(&session->stop_mutex); } return SR_OK; @@ -414,6 +414,10 @@ SR_API int sr_session_run(void) sr_session_iteration(TRUE); } + g_mutex_lock(&session->stop_mutex); + session->running = FALSE; + session->abort_session = FALSE; + g_mutex_unlock(&session->stop_mutex); return SR_OK; } @@ -447,7 +451,6 @@ SR_PRIV int sr_session_stop_sync(void) sdi->driver->dev_acquisition_stop(sdi, NULL); } } - session->running = FALSE; return SR_OK; } @@ -472,9 +475,10 @@ SR_API int sr_session_stop(void) return SR_ERR_BUG; } -// g_mutex_lock(&session->stop_mutex); - session->abort_session = TRUE; -// g_mutex_unlock(&session->stop_mutex); + g_mutex_lock(&session->stop_mutex); + if (session->running) + session->abort_session = TRUE; + g_mutex_unlock(&session->stop_mutex); return SR_OK; } @@ -722,29 +726,37 @@ static int _sr_session_source_remove(gintptr poll_object) if (old == session->num_sources) return SR_OK; - session->num_sources -= 1; + session->num_sources -= 1; - if (old != session->num_sources) { - memmove(&session->pollfds[old], &session->pollfds[old+1], - (session->num_sources - old) * sizeof(GPollFD)); - memmove(&session->sources[old], &session->sources[old+1], - (session->num_sources - old) * sizeof(struct source)); - } + if (session->num_sources == 0) { + session->source_timeout = -1; + g_free(session->pollfds); + g_free(session->sources); + session->pollfds = NULL; + session->sources = NULL; + } else { + if (old != session->num_sources) { + memmove(&session->pollfds[old], &session->pollfds[old+1], + (session->num_sources - old) * sizeof(GPollFD)); + memmove(&session->sources[old], &session->sources[old+1], + (session->num_sources - old) * sizeof(struct source)); + } - new_pollfds = g_try_realloc(session->pollfds, sizeof(GPollFD) * session->num_sources); - if (!new_pollfds && session->num_sources > 0) { - sr_err("%s: new_pollfds malloc failed", __func__); - return SR_ERR_MALLOC; - } + new_pollfds = g_try_realloc(session->pollfds, sizeof(GPollFD) * session->num_sources); + if (!new_pollfds && session->num_sources > 0) { + sr_err("%s: new_pollfds malloc failed", __func__); + return SR_ERR_MALLOC; + } - new_sources = g_try_realloc(session->sources, sizeof(struct source) * session->num_sources); - if (!new_sources && session->num_sources > 0) { - sr_err("%s: new_sources malloc failed", __func__); - return SR_ERR_MALLOC; - } + new_sources = g_try_realloc(session->sources, sizeof(struct source) * session->num_sources); + if (!new_sources && session->num_sources > 0) { + sr_err("%s: new_sources malloc failed", __func__); + return SR_ERR_MALLOC; + } - session->pollfds = new_pollfds; - session->sources = new_sources; + session->pollfds = new_pollfds; + session->sources = new_sources; + } return SR_OK; } diff --git a/libsigrok4DSL/trigger.c b/libsigrok4DSL/trigger.c index 6baf5d7e..0f572640 100644 --- a/libsigrok4DSL/trigger.c +++ b/libsigrok4DSL/trigger.c @@ -34,7 +34,7 @@ * @{ */ -struct ds_trigger *trigger; +struct ds_trigger *trigger = NULL; /** * recovery trigger to initial status. @@ -77,7 +77,7 @@ SR_API int ds_trigger_destroy(void) { if (trigger) g_free(trigger); - + trigger = NULL; return SR_OK; } @@ -214,7 +214,10 @@ SR_API int ds_trigger_set_en(uint16_t enable) */ SR_API uint16_t ds_trigger_get_en() { - return trigger->trigger_en; + if (trigger == NULL) + return 0; + else + return trigger->trigger_en; } /** From aa9d7c400b92545644e807dfdca5d2d0d6ace835 Mon Sep 17 00:00:00 2001 From: DreamSourceLab Date: Tue, 14 Jun 2016 22:52:27 +0800 Subject: [PATCH 22/32] Add multibytes search feature for protocol list viewer;Add absolute time to session file;fix other minor issues --- DSView/main.cpp | 14 +-- DSView/pv/data/decoderstack.cpp | 10 +- DSView/pv/data/decoderstack.h | 2 +- DSView/pv/device/devinst.cpp | 7 ++ DSView/pv/device/devinst.h | 7 ++ DSView/pv/devicemanager.cpp | 9 +- DSView/pv/dock/dsotriggerdock.cpp | 2 +- DSView/pv/dock/protocoldock.cpp | 110 +++++++++++++++++--- DSView/pv/dock/protocoldock.h | 1 + DSView/pv/dock/triggerdock.cpp | 4 +- DSView/pv/mainwindow.cpp | 44 ++++---- DSView/pv/mainwindow.h | 2 + DSView/pv/sigsession.cpp | 58 ++++++++--- DSView/pv/sigsession.h | 7 ++ DSView/pv/toolbars/filebar.cpp | 19 ++-- DSView/pv/toolbars/samplingbar.cpp | 23 +---- DSView/pv/toolbars/trigbar.cpp | 38 ------- DSView/pv/view/dsosignal.cpp | 22 ++-- DSView/pv/view/view.cpp | 94 +++++++++-------- DSView/pv/view/view.h | 16 ++- DSView/pv/view/viewport.cpp | 144 ++++++++++++++++++--------- DSView/pv/view/viewport.h | 7 +- DSView/pv/widgets/viewstatus.cpp | 66 ++++++++++++ DSView/pv/widgets/viewstatus.h | 58 +++++++++++ libsigrok4DSL/hardware/DSL/dscope.c | 26 ++--- libsigrok4DSL/hardware/DSL/dslogic.c | 48 +++------ libsigrok4DSL/hardware/demo/demo.c | 18 +++- libsigrok4DSL/libsigrok.h | 14 ++- libsigrok4DSL/proto.h | 2 +- libsigrok4DSL/session_driver.c | 34 ++++++- libsigrok4DSL/session_file.c | 33 ++++-- 31 files changed, 605 insertions(+), 334 deletions(-) create mode 100644 DSView/pv/widgets/viewstatus.cpp create mode 100644 DSView/pv/widgets/viewstatus.h diff --git a/DSView/main.cpp b/DSView/main.cpp index 07bf54a7..58b022b5 100644 --- a/DSView/main.cpp +++ b/DSView/main.cpp @@ -62,20 +62,9 @@ int main(int argc, char *argv[]) DSApplication a(argc, argv); - // Language -#ifdef LANGUAGE_ZH_CN - QTranslator qtTrans; - qtTrans.load(":/qt_zh_CN"); - a.installTranslator(&qtTrans); - - QTranslator DSViewTrans; - DSViewTrans.load(":/DSView_zh"); - a.installTranslator(&DSViewTrans); -#endif - // Set some application metadata QApplication::setApplicationVersion(DS_VERSION_STRING); - QApplication::setApplicationName("DSView(Beta)"); + QApplication::setApplicationName("DSView"); QApplication::setOrganizationDomain("http://www.DreamSourceLab.com"); // Parse arguments @@ -130,7 +119,6 @@ int main(int argc, char *argv[]) } do { - #ifdef ENABLE_DECODE // Initialise libsigrokdecode if (srd_init(NULL) != SRD_OK) { diff --git a/DSView/pv/data/decoderstack.cpp b/DSView/pv/data/decoderstack.cpp index e0b64ab9..58827a03 100644 --- a/DSView/pv/data/decoderstack.cpp +++ b/DSView/pv/data/decoderstack.cpp @@ -60,7 +60,7 @@ const int64_t DecoderStack::DecodeChunkLength = 4 * 1024; //const int64_t DecoderStack::DecodeChunkLength = 1024 * 1024; const unsigned int DecoderStack::DecodeNotifyPeriod = 1024; -//mutex DecoderStack::_global_decode_mutex; +mutex DecoderStack::_global_decode_mutex; DecoderStack::DecoderStack(pv::SigSession &session, const srd_decoder *const dec) : @@ -145,12 +145,11 @@ void DecoderStack::build_row() _rows[row] = decode::RowData(); std::map::const_iterator iter = _rows_gshow.find(row); if (iter == _rows_gshow.end()) { + _rows_gshow[row] = true; if (row.title().contains("bit", Qt::CaseInsensitive) || row.title().contains("warning", Qt::CaseInsensitive)) { - _rows_gshow[row] = false; _rows_lshow[row] = false; } else { - _rows_gshow[row] = true; _rows_lshow[row] = true; } } @@ -169,12 +168,11 @@ void DecoderStack::build_row() _rows[row] = decode::RowData(); std::map::const_iterator iter = _rows_gshow.find(row); if (iter == _rows_gshow.end()) { + _rows_gshow[row] = true; if (row.title().contains("bit", Qt::CaseInsensitive) || row.title().contains("warning", Qt::CaseInsensitive)) { - _rows_gshow[row] = false; _rows_lshow[row] = false; } else { - _rows_gshow[row] = true; _rows_lshow[row] = true; } } @@ -553,7 +551,7 @@ void DecoderStack::decode_data( void DecoderStack::decode_proc() { - //lock_guard decode_lock(_global_decode_mutex); + lock_guard decode_lock(_global_decode_mutex); optional sample_count; srd_session *session; diff --git a/DSView/pv/data/decoderstack.h b/DSView/pv/data/decoderstack.h index 8f38d361..8e0d882f 100644 --- a/DSView/pv/data/decoderstack.h +++ b/DSView/pv/data/decoderstack.h @@ -169,7 +169,7 @@ private: * @todo A proper solution should be implemented to allow multiple * decode operations. */ - //static boost::mutex _global_decode_mutex; + static boost::mutex _global_decode_mutex; std::list< boost::shared_ptr > _stack; diff --git a/DSView/pv/device/devinst.cpp b/DSView/pv/device/devinst.cpp index 731df1fc..347ce869 100644 --- a/DSView/pv/device/devinst.cpp +++ b/DSView/pv/device/devinst.cpp @@ -183,6 +183,13 @@ GSList* DevInst::get_dev_mode_list() return sr_dev_mode_list(sdi); } +QString DevInst::name() +{ + sr_dev_inst *const sdi = dev_inst(); + assert(sdi); + return QString::fromLocal8Bit(sdi->driver->name); +} + bool DevInst::is_trigger_enabled() const { return false; diff --git a/DSView/pv/device/devinst.h b/DSView/pv/device/devinst.h index da441a7f..c5e58d62 100644 --- a/DSView/pv/device/devinst.h +++ b/DSView/pv/device/devinst.h @@ -109,6 +109,13 @@ public: */ GSList* get_dev_mode_list(); + /** + * @brief Get the device name from the driver + * + * @return device name + */ + QString name(); + virtual bool is_trigger_enabled() const; public: diff --git a/DSView/pv/devicemanager.cpp b/DSView/pv/devicemanager.cpp index 474db42b..45ebbd4c 100644 --- a/DSView/pv/devicemanager.cpp +++ b/DSView/pv/devicemanager.cpp @@ -47,7 +47,7 @@ using std::ostringstream; using std::runtime_error; using std::string; -char config_path[256]; +extern char AppDataPath[256]; namespace pv { @@ -103,12 +103,9 @@ std::list > DeviceManager::driver_scan( // Check If DSL hardware driver if (strncmp(driver->name, "virtual", 7)) { - QDir dir(QCoreApplication::applicationDirPath()); - if (!dir.cd("res")) + QDir dir(DS_RES_PATH); + if (!dir.exists()) return driver_devices; - QString str = dir.absolutePath() + "/"; - QString str_utf8 = QString::fromLocal8Bit(str.toLocal8Bit()); - strcpy(config_path, str_utf8.toUtf8().data()); } // Do the scan diff --git a/DSView/pv/dock/dsotriggerdock.cpp b/DSView/pv/dock/dsotriggerdock.cpp index 346b7ea4..f7746d63 100644 --- a/DSView/pv/dock/dsotriggerdock.cpp +++ b/DSView/pv/dock/dsotriggerdock.cpp @@ -243,7 +243,7 @@ void DsoTriggerDock::type_changed() void DsoTriggerDock::device_change() { - if (strcmp(_session.get_device()->dev_inst()->driver->name, "DSLogic") != 0) { + if (_session.get_device()->name() != "DSLogic") { position_spinBox->setDisabled(true); position_slider->setDisabled(true); } else { diff --git a/DSView/pv/dock/protocoldock.cpp b/DSView/pv/dock/protocoldock.cpp index b93a13a6..27f83a83 100644 --- a/DSView/pv/dock/protocoldock.cpp +++ b/DSView/pv/dock/protocoldock.cpp @@ -39,6 +39,7 @@ #include #include #include +#include #include #include @@ -544,16 +545,54 @@ void ProtocolDock::search_pre() { // now the proxy only contains rows that match the name // let's take the pre one and map it to the original model - if (_model_proxy.rowCount() == 0) + if (_model_proxy.rowCount() == 0) { + _table_view->scrollToTop(); + _table_view->clearSelection(); + _matchs_label->setText(QString::number(0)); + _cur_search_index = -1; return; - _cur_search_index -= 1; - if (_cur_search_index <= -1 || _cur_search_index >= _model_proxy.rowCount()) - _cur_search_index = _model_proxy.rowCount() - 1; - QModelIndex matchingIndex = _model_proxy.mapToSource(_model_proxy.index(ceil(_cur_search_index),_model_proxy.filterKeyColumn())); - if(matchingIndex.isValid()){ + } + int i; + uint64_t rowCount = _model_proxy.rowCount(); + QModelIndex matchingIndex; + pv::data::DecoderModel *decoder_model = _session.get_decoder_model(); + boost::shared_ptr decoder_stack = decoder_model->getDecoderStack(); + do { + _cur_search_index--; + if (_cur_search_index <= -1 || _cur_search_index >= _model_proxy.rowCount()) + _cur_search_index = _model_proxy.rowCount() - 1; + + matchingIndex = _model_proxy.mapToSource(_model_proxy.index(ceil(_cur_search_index),_model_proxy.filterKeyColumn())); + if (!decoder_stack || !matchingIndex.isValid()) + break; + i = 1; + uint64_t row = matchingIndex.row() + 1; + uint64_t col = matchingIndex.column(); + pv::data::decode::Annotation ann; + bool ann_valid; + while(i < _str_list.size()) { + QString nxt = _str_list.at(i); + do { + ann_valid = decoder_stack->list_annotation(ann, col, row); + row++; + }while(ann_valid && (ann.type() < 100 || ann.type() > 999)); + QString source = ann.annotations().at(0); + if (ann_valid && source.contains(nxt)) + i++; + else + break; + } + }while(i < _str_list.size() && --rowCount); + + if(i >= _str_list.size() && matchingIndex.isValid()){ _table_view->scrollTo(matchingIndex); _table_view->setCurrentIndex(matchingIndex); _table_view->clicked(matchingIndex); + } else { + _table_view->scrollToTop(); + _table_view->clearSelection(); + _matchs_label->setText(QString::number(0)); + _cur_search_index = -1; } } @@ -561,24 +600,67 @@ void ProtocolDock::search_nxt() { // now the proxy only contains rows that match the name // let's take the pre one and map it to the original model - if (_model_proxy.rowCount() == 0) + if (_model_proxy.rowCount() == 0) { + _table_view->scrollToTop(); + _table_view->clearSelection(); + _matchs_label->setText(QString::number(0)); + _cur_search_index = -1; return; - _cur_search_index += 1; - if (_cur_search_index < 0 || _cur_search_index >= _model_proxy.rowCount()) - _cur_search_index = 0; - QModelIndex matchingIndex = _model_proxy.mapToSource(_model_proxy.index(floor(_cur_search_index),_model_proxy.filterKeyColumn())); - if(matchingIndex.isValid()){ + } + int i; + uint64_t rowCount = _model_proxy.rowCount(); + QModelIndex matchingIndex; + pv::data::DecoderModel *decoder_model = _session.get_decoder_model(); + boost::shared_ptr decoder_stack = decoder_model->getDecoderStack(); + do { + _cur_search_index++; + if (_cur_search_index < 0 || _cur_search_index >= _model_proxy.rowCount()) + _cur_search_index = 0; + + matchingIndex = _model_proxy.mapToSource(_model_proxy.index(floor(_cur_search_index),_model_proxy.filterKeyColumn())); + if (!decoder_stack || !matchingIndex.isValid()) + break; + i = 1; + uint64_t row = matchingIndex.row() + 1; + uint64_t col = matchingIndex.column(); + pv::data::decode::Annotation ann; + bool ann_valid; + while(i < _str_list.size()) { + QString nxt = _str_list.at(i); + do { + ann_valid = decoder_stack->list_annotation(ann, col, row); + row++; + }while(ann_valid && (ann.type() < 100 || ann.type() > 999)); + QString source = ann.annotations().at(0); + if (ann_valid && source.contains(nxt)) + i++; + else + break; + } + }while(i < _str_list.size() && --rowCount); + + if(i >= _str_list.size() && matchingIndex.isValid()){ _table_view->scrollTo(matchingIndex); _table_view->setCurrentIndex(matchingIndex); _table_view->clicked(matchingIndex); + } else { + _table_view->scrollToTop(); + _table_view->clearSelection(); + _matchs_label->setText(QString::number(0)); + _cur_search_index = -1; } } void ProtocolDock::search_done() { QString str = _search_edit->text().trimmed(); - _model_proxy.setFilterFixedString(str); - _matchs_label->setText(QString::number(_model_proxy.rowCount())); + QRegExp rx("(-)"); + _str_list = str.split(rx); + _model_proxy.setFilterFixedString(_str_list.first()); + if (_str_list.size() > 1) + _matchs_label->setText("..."); + else + _matchs_label->setText(QString::number(_model_proxy.rowCount())); } diff --git a/DSView/pv/dock/protocoldock.h b/DSView/pv/dock/protocoldock.h index 71f68db6..bcf4bffd 100644 --- a/DSView/pv/dock/protocoldock.h +++ b/DSView/pv/dock/protocoldock.h @@ -89,6 +89,7 @@ private: SigSession &_session; QSortFilterProxyModel _model_proxy; double _cur_search_index; + QStringList _str_list; QSplitter *_split_widget; QWidget *_up_widget; diff --git a/DSView/pv/dock/triggerdock.cpp b/DSView/pv/dock/triggerdock.cpp index 26fbdfa9..5dcb218a 100644 --- a/DSView/pv/dock/triggerdock.cpp +++ b/DSView/pv/dock/triggerdock.cpp @@ -285,7 +285,7 @@ void TriggerDock::simple_trigger() void TriggerDock::adv_trigger() { - if (strcmp(_session.get_device()->dev_inst()->driver->name, "DSLogic") == 0) { + if (_session.get_device()->name() == "DSLogic") { bool stream = false; GVariant *gvar = _session.get_device()->get_config(NULL, NULL, SR_CONF_STREAM); if (gvar != NULL) { @@ -368,7 +368,7 @@ void TriggerDock::device_change() g_variant_unref(gvar); } - if (!strncmp(_session.get_device()->dev_inst()->driver->name, "virtual", 7) || + if (_session.get_device()->name().contains("virtual") || stream) { simple_radioButton->setChecked(true); simple_trigger(); diff --git a/DSView/pv/mainwindow.cpp b/DSView/pv/mainwindow.cpp index 7dd2e349..5dc78610 100644 --- a/DSView/pv/mainwindow.cpp +++ b/DSView/pv/mainwindow.cpp @@ -87,6 +87,8 @@ using boost::dynamic_pointer_cast; using std::list; using std::vector; +extern char AppDataPath[256]; + namespace pv { MainWindow::MainWindow(DeviceManager &device_manager, @@ -316,16 +318,17 @@ void MainWindow::update_device_list() errorMessage, infoMessage)); } - if (strncmp(selected_device->dev_inst()->driver->name, "virtual", 7)) { + if (!selected_device->name().contains("virtual")) { _logo_bar->dsl_connected(true); - QString ses_name = config_path + - QString::fromUtf8(selected_device->dev_inst()->driver->name) + + QString ses_name = DS_RES_PATH + + selected_device->name() + QString::number(selected_device->dev_inst()->mode) + ".dsc"; load_session(ses_name); } else { _logo_bar->dsl_connected(false); } + _view->status_clear(); } void MainWindow::reload() @@ -386,6 +389,8 @@ void MainWindow::device_detach() if (_session.get_capture_state() == SigSession::Running) _session.stop_capture(); + session_save(); + struct sr_dev_driver **const drivers = sr_driver_list(); struct sr_dev_driver **driver; for (driver = drivers; *driver; driver++) @@ -488,40 +493,25 @@ void MainWindow::capture_state_changed(int state) _sampling_bar->enable_toggle(state != SigSession::Running); _trig_bar->enable_toggle(state != SigSession::Running); _measure_dock->widget()->setEnabled(state != SigSession::Running); - if (_session.get_device()->dev_inst()->mode == LOGIC && - state == SigSession::Stopped) { - GVariant *gvar = _session.get_device()->get_config(NULL, NULL, SR_CONF_RLE); - if (gvar != NULL) { - bool rle = g_variant_get_boolean(gvar); - g_variant_unref(gvar); - if (rle) { - gvar = _session.get_device()->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()) { - show_session_error(tr("RLE Mode Warning"), - tr("Hardware buffer is full!\nActually received samples is less than setted sample depth!")); - } - } - } - } - } - } } -void MainWindow::closeEvent(QCloseEvent *event) +void MainWindow::session_save() { - QDir dir(QCoreApplication::applicationDirPath()); - if (dir.cd("res")) { - QString driver_name = _session.get_device()->dev_inst()->driver->name; + QDir dir(DS_RES_PATH); + if (dir.exists()) { + QString driver_name = _session.get_device()->name(); QString mode_name = QString::number(_session.get_device()->dev_inst()->mode); QString file_name = dir.absolutePath() + "/" + driver_name + mode_name + ".dsc"; if (strncmp(driver_name.toLocal8Bit(), "virtual", 7) && !file_name.isEmpty()) store_session(file_name); } +} + +void MainWindow::closeEvent(QCloseEvent *event) +{ + session_save(); event->accept(); } diff --git a/DSView/pv/mainwindow.h b/DSView/pv/mainwindow.h index 1b96a375..5159d077 100644 --- a/DSView/pv/mainwindow.h +++ b/DSView/pv/mainwindow.h @@ -86,6 +86,8 @@ private: bool eventFilter(QObject *object, QEvent *event); + void session_save(); + private slots: void load_file(QString file_name); diff --git a/DSView/pv/sigsession.cpp b/DSView/pv/sigsession.cpp index 53eca6cd..3abb5eac 100644 --- a/DSView/pv/sigsession.cpp +++ b/DSView/pv/sigsession.cpp @@ -232,7 +232,7 @@ void SigSession::save_file(const QString name, int type){ } sr_session_save(name.toLocal8Bit().data(), _dev_inst->dev_inst(), - data, unit_size, sample_count); + data, unit_size, sample_count, _trigger_time.toMSecsSinceEpoch(), _trigger_pos); } QList SigSession::getSuportedExportFormats(){ @@ -408,8 +408,7 @@ void SigSession::set_default_device(boost::function error_ // Try and find the DreamSourceLab device and select that by default BOOST_FOREACH (boost::shared_ptr dev, devices) if (dev->dev_inst() && - strncmp(dev->dev_inst()->driver->name, - "virtual", 7) != 0) { + !dev->name().contains("virtual")) { default_device = dev; break; } @@ -530,7 +529,7 @@ void SigSession::start_capture(bool instant, } // update setting - if (strcmp(_dev_inst->dev_inst()->driver->name, "virtual-session")) + if (_dev_inst->name() != "virtual-session") _instant = instant; else _instant = true; @@ -674,10 +673,6 @@ void SigSession::check_update() } } -void SigSession::feed_in_header(const sr_dev_inst *sdi) -{ -} - void SigSession::add_group() { std::list probe_index_list; @@ -909,7 +904,7 @@ void SigSession::refresh(int holdtime) { boost::lock_guard lock(_data_mutex); - if (strncmp(_dev_inst->dev_inst()->driver->name, "virtual", 7)) { + if (!_dev_inst->name().contains("virtual")) { _data_lock = true; _refresh_timer.start(holdtime); } @@ -951,6 +946,26 @@ bool SigSession::get_data_lock() return _data_lock; } +void SigSession::feed_in_header(const sr_dev_inst *sdi) +{ + _trigger_pos = 0; + _trigger_time = QDateTime::currentDateTime(); + const int64_t secs = -cur_sampletime(); + _trigger_time = _trigger_time.addSecs(secs); + + if (_dev_inst->name() == "virtual-session") { + int64_t time; + GVariant* gvar = _dev_inst->get_config(NULL, NULL, SR_CONF_TRIGGER_TIME); + if (gvar != NULL) { + time = g_variant_get_int64(gvar); + g_variant_unref(gvar); + if (time != 0) + _trigger_time = QDateTime::fromMSecsSinceEpoch(time); + } + } + receive_header(); +} + void SigSession::feed_in_meta(const sr_dev_inst *sdi, const sr_datafeed_meta &meta) { @@ -973,7 +988,14 @@ void SigSession::feed_in_meta(const sr_dev_inst *sdi, void SigSession::feed_in_trigger(const ds_trigger_pos &trigger_pos) { if (_dev_inst->dev_inst()->mode != DSO) { - receive_trigger(trigger_pos.real_pos); + _trigger_pos = trigger_pos.real_pos; + receive_trigger(_trigger_pos); + if (_dev_inst->name() != "virtual-session") { + const double time = trigger_pos.real_pos * 1.0 / _cur_samplerate; + _trigger_time = QDateTime::currentDateTime(); + const int64_t secs = time - cur_sampletime(); + _trigger_time = _trigger_time.addSecs(secs); + } } else { int probe_count = 0; int probe_en_count = 0; @@ -986,7 +1008,8 @@ void SigSession::feed_in_trigger(const ds_trigger_pos &trigger_pos) probe_en_count++; } } - receive_trigger(trigger_pos.real_pos * probe_count / probe_en_count); + _trigger_pos = trigger_pos.real_pos * probe_count / probe_en_count; + receive_trigger(_trigger_pos); } } @@ -1218,9 +1241,6 @@ void SigSession::hotplug_proc(boost::function error_handle if (_hot_detach) { qDebug("DreamSourceLab hardware detached!"); device_detach(); - _logic_data.reset(); - _dso_data.reset(); - _analog_data.reset(); _hot_detach = false; } boost::this_thread::sleep(boost::posix_time::millisec(100)); @@ -1487,4 +1507,14 @@ vector< boost::shared_ptr > SigSession::get_math_signals() return _math_traces; } +QDateTime SigSession::get_trigger_time() const +{ + return _trigger_time; +} + +uint64_t SigSession::get_trigger_pos() const +{ + return _trigger_pos; +} + } // namespace pv diff --git a/DSView/pv/sigsession.h b/DSView/pv/sigsession.h index 8ff3e0d9..5a9d8fa8 100644 --- a/DSView/pv/sigsession.h +++ b/DSView/pv/sigsession.h @@ -131,6 +131,8 @@ public: uint64_t cur_samplerate() const; uint64_t cur_samplelimits() const; double cur_sampletime() const; + QDateTime get_trigger_time() const; + uint64_t get_trigger_pos() const; void start_capture(bool instant, boost::function error_handler); @@ -274,6 +276,9 @@ private: bool _data_lock; bool _data_updated; + QDateTime _trigger_time; + uint64_t _trigger_pos; + signals: void capture_state_changed(int state); @@ -292,6 +297,8 @@ signals: void receive_trigger(quint64 trigger_pos); + void receive_header(); + void dso_ch_changed(uint16_t num); void frame_began(); diff --git a/DSView/pv/toolbars/filebar.cpp b/DSView/pv/toolbars/filebar.cpp index ecf93a5a..6007ad66 100644 --- a/DSView/pv/toolbars/filebar.cpp +++ b/DSView/pv/toolbars/filebar.cpp @@ -34,6 +34,8 @@ #include +extern char AppDataPath[256]; + namespace pv { namespace toolbars { @@ -109,11 +111,7 @@ FileBar::FileBar(SigSession &session, QWidget *parent) : connect(_action_capture, SIGNAL(triggered()), this, SLOT(on_actionCapture_triggered())); _file_button.setPopupMode(QToolButton::InstantPopup); -#ifdef LANGUAGE_ZH_CN - _file_button.setIcon(QIcon(":/icons/file_cn.png")); -#else _file_button.setIcon(QIcon(":/icons/file.png")); -#endif _menu = new QMenu(this); _menu->addMenu(_menu_session); @@ -226,8 +224,8 @@ void FileBar::on_actionLoad_triggered() void FileBar::on_actionDefault_triggered() { - QDir dir(QCoreApplication::applicationDirPath()); - if (!dir.cd("res")) { + QDir dir(DS_RES_PATH); + if (!dir.exists()) { QMessageBox msg(this); msg.setText(tr("Session Load")); msg.setInformativeText(tr("Cannot find default session file for this device!")); @@ -237,7 +235,7 @@ void FileBar::on_actionDefault_triggered() return; } - QString driver_name = _session.get_device()->dev_inst()->driver->name; + QString driver_name = _session.get_device()->name(); QString mode_name = QString::number(_session.get_device()->dev_inst()->mode); QString file_name = dir.absolutePath() + "/" + driver_name + mode_name + ".def.dsc"; if (!file_name.isEmpty()) @@ -246,7 +244,7 @@ void FileBar::on_actionDefault_triggered() void FileBar::on_actionStore_triggered() { - QString default_name = _session.get_device()->dev_inst()->driver->name; + QString default_name = _session.get_device()->name(); QString file_name = QFileDialog::getSaveFileName( this, tr("Save Session"), default_name, tr("DSView Session (*.dsc)")); @@ -266,13 +264,8 @@ void FileBar::on_actionCapture_triggered() void FileBar::enable_toggle(bool enable) { _file_button.setDisabled(!enable); -#ifdef LANGUAGE_ZH_CN - _file_button.setIcon(enable ? QIcon(":/icons/file_cn.png") : - QIcon(":/icons/file_dis_cn.png")); -#else _file_button.setIcon(enable ? QIcon(":/icons/file.png") : QIcon(":/icons/file_dis.png")); -#endif } } // namespace toolbars diff --git a/DSView/pv/toolbars/samplingbar.cpp b/DSView/pv/toolbars/samplingbar.cpp index c3bfd339..15cf2c81 100644 --- a/DSView/pv/toolbars/samplingbar.cpp +++ b/DSView/pv/toolbars/samplingbar.cpp @@ -87,19 +87,11 @@ SamplingBar::SamplingBar(SigSession &session, QWidget *parent) : _sample_rate(this), _updating_sample_rate(false), _updating_sample_count(false), - #ifdef LANGUAGE_ZH_CN - _icon_stop(":/icons/stop_cn.png"), - _icon_start(":/icons/start_cn.png"), - _icon_instant(":/icons/instant_cn.png"), - _icon_start_dis(":/icons/start_dis_cn.png"), - _icon_instant_dis(":/icons/instant_dis_cn.png"), - #else _icon_stop(":/icons/stop.png"), _icon_start(":/icons/start.png"), _icon_instant(":/icons/instant.png"), _icon_start_dis(":/icons/start_dis.png"), _icon_instant_dis(":/icons/instant_dis.png"), - #endif _run_stop_button(this), _instant_button(this), _instant(false) @@ -115,13 +107,9 @@ SamplingBar::SamplingBar(SigSession &session, QWidget *parent) : connect(&_instant_button, SIGNAL(clicked()), this, SLOT(on_instant_stop())); -#ifdef LANGUAGE_ZH_CN - _configure_button.setIcon(QIcon::fromTheme("configure", - QIcon(":/icons/params_cn.png"))); -#else _configure_button.setIcon(QIcon::fromTheme("configure", QIcon(":/icons/params.png"))); -#endif + _run_stop_button.setIcon(_icon_start); _instant_button.setIcon(_icon_instant); @@ -337,13 +325,8 @@ void SamplingBar::set_sampling(bool sampling) } _configure_button.setEnabled(!sampling); -#ifdef LANGUAGE_ZH_CN - _configure_button.setIcon(sampling ? QIcon(":/icons/params_dis_cn.png") : - QIcon(":/icons/params_cn.png")); -#else _configure_button.setIcon(sampling ? QIcon(":/icons/params_dis.png") : QIcon(":/icons/params.png")); -#endif } void SamplingBar::set_sample_rate(uint64_t sample_rate) @@ -492,7 +475,7 @@ void SamplingBar::on_samplecount_sel(int index) boost::shared_ptr _devInst = get_selected_device(); assert(_devInst); - if (strcmp(_devInst->dev_inst()->driver->name, "DSLogic") == 0 && _devInst->dev_inst()->mode != DSO) { + if (_devInst->name() == "DSLogic" && _devInst->dev_inst()->mode != DSO) { // Set the sample count _devInst->set_config(NULL, NULL, @@ -517,7 +500,7 @@ void SamplingBar::on_samplerate_sel(int index) // Get last samplerate //last_sample_rate = get_selected_device()->get_sample_rate(); - if (strcmp(_devInst->dev_inst()->driver->name, "DSLogic") == 0 && _devInst->dev_inst()->mode != DSO) { + if (_devInst->name() == "DSLogic" && _devInst->dev_inst()->mode != DSO) { // Set the samplerate get_selected_device()->set_config(NULL, NULL, SR_CONF_SAMPLERATE, diff --git a/DSView/pv/toolbars/trigbar.cpp b/DSView/pv/toolbars/trigbar.cpp index 509b28fc..5a938e29 100644 --- a/DSView/pv/toolbars/trigbar.cpp +++ b/DSView/pv/toolbars/trigbar.cpp @@ -52,24 +52,6 @@ TrigBar::TrigBar(SigSession &session, QWidget *parent) : connect(&_search_button, SIGNAL(clicked()), this, SLOT(search_clicked())); -#ifdef LANGUAGE_ZH_CN - _trig_button.setIcon(QIcon::fromTheme("trig", - QIcon(":/icons/trigger_cn.png"))); - _trig_button.setCheckable(true); - _protocol_button.setIcon(QIcon::fromTheme("trig", - QIcon(":/icons/protocol_cn.png"))); -#ifdef ENABLE_DECODE - _protocol_button.setCheckable(true); -#endif - _measure_button.setIcon(QIcon::fromTheme("trig", - QIcon(":/icons/measure_cn.png"))); - _measure_button.setCheckable(true); - _search_button.setIcon(QIcon::fromTheme("trig", - QIcon(":/icons/search-bar_cn.png"))); - _search_button.setCheckable(true); - _math_button.setIcon(QIcon::fromTheme("trig", - QIcon(":/icons/math_cn.png"))); -#else _trig_button.setIcon(QIcon::fromTheme("trig", QIcon(":/icons/trigger.png"))); _trig_button.setCheckable(true); @@ -86,7 +68,6 @@ TrigBar::TrigBar(SigSession &session, QWidget *parent) : _search_button.setCheckable(true); _math_button.setIcon(QIcon::fromTheme("trig", QIcon(":/icons/math.png"))); -#endif _action_fft = new QAction(this); _action_fft->setText(QApplication::translate( @@ -142,18 +123,6 @@ void TrigBar::enable_toggle(bool enable) _search_button.setDisabled(!enable); _math_button.setDisabled(!enable); -#ifdef LANGUAGE_ZH_CN - _trig_button.setIcon(enable ? QIcon::fromTheme("trig", QIcon(":/icons/trigger_cn.png")) : - QIcon::fromTheme("trig", QIcon(":/icons/trigger_dis_cn.png"))); - _protocol_button.setIcon(enable ? QIcon::fromTheme("trig", QIcon(":/icons/protocol_cn.png")) : - QIcon::fromTheme("trig", QIcon(":/icons/protocol_dis_cn.png"))); - _measure_button.setIcon(enable ? QIcon::fromTheme("trig", QIcon(":/icons/measure_cn.png")) : - QIcon::fromTheme("trig", QIcon(":/icons/measure_dis_cn.png"))); - _search_button.setIcon(enable ? QIcon::fromTheme("trig", QIcon(":/icons/search-bar_cn.png")) : - QIcon::fromTheme("trig", QIcon(":/icons/search-bar_dis_cn.png"))); - _math_button.setIcon(enable ? QIcon::fromTheme("trig", QIcon(":/icons/math_cn.png")) : - QIcon::fromTheme("trig", QIcon(":/icons/math_dis_cn.png"))); -#else _trig_button.setIcon(enable ? QIcon::fromTheme("trig", QIcon(":/icons/trigger.png")) : QIcon::fromTheme("trig", QIcon(":/icons/trigger_dis.png"))); _protocol_button.setIcon(enable ? QIcon::fromTheme("trig", QIcon(":/icons/protocol.png")) : @@ -164,20 +133,13 @@ void TrigBar::enable_toggle(bool enable) QIcon::fromTheme("trig", QIcon(":/icons/search-bar_dis.png"))); _math_button.setIcon(enable ? QIcon::fromTheme("trig", QIcon(":/icons/math.png")) : QIcon::fromTheme("trig", QIcon(":/icons/math_dis.png"))); - -#endif } void TrigBar::enable_protocol(bool enable) { _protocol_button.setDisabled(!enable); -#ifdef LANGUAGE_ZH_CN - _protocol_button.setIcon(enable ? QIcon::fromTheme("trig", QIcon(":/icons/protocol_cn.png")) : - QIcon::fromTheme("trig", QIcon(":/icons/protocol_dis_cn.png"))); -#else _protocol_button.setIcon(enable ? QIcon::fromTheme("trig", QIcon(":/icons/protocol.png")) : QIcon::fromTheme("trig", QIcon(":/icons/protocol_dis.png"))); -#endif } void TrigBar::close_all() diff --git a/DSView/pv/view/dsosignal.cpp b/DSView/pv/view/dsosignal.cpp index 24916a87..7f4b37ac 100644 --- a/DSView/pv/view/dsosignal.cpp +++ b/DSView/pv/view/dsosignal.cpp @@ -199,7 +199,7 @@ float DsoSignal::get_scale() void DsoSignal::set_enable(bool enable) { - if ((strcmp(_dev_inst->dev_inst()->driver->name, "DSLogic") == 0) && + if (_dev_inst->name() == "DSLogic" && get_index() == 0) return; _view->session().refresh(INT_MAX); @@ -524,7 +524,7 @@ bool DsoSignal::load_settings() qDebug() << "ERROR: config_get SR_CONF_TRIGGER_VALUE failed."; return false; } - bool isDSCope = (strcmp(_dev_inst->dev_inst()->driver->name, "DSCope") == 0); + bool isDSCope = (_dev_inst->name() == "DSCope"); if (isDSCope) { _trig_vpos = min(max(trigger_value/255.0, 0+TrigMargin), 1-TrigMargin); } else { @@ -617,7 +617,7 @@ void DsoSignal::set_trig_vpos(int pos) int trig_value; if (enabled()) { double delta = min((double)max(pos - UpMargin, 0), get_view_rect().height()) * 1.0 / get_view_rect().height(); - bool isDSCope = (strcmp(_dev_inst->dev_inst()->driver->name, "DSCope") == 0); + bool isDSCope = (_dev_inst->name() == "DSCope"); if (isDSCope) { trig_value = delta * 255.0 + 0.5; _trig_vpos = min(max(trig_value/255.0, 0+TrigMargin), 1-TrigMargin); @@ -637,7 +637,7 @@ void DsoSignal::set_trigRate(double rate) { int trig_value; double delta = rate; - bool isDSCope = (strcmp(_dev_inst->dev_inst()->driver->name, "DSCope") == 0); + bool isDSCope = (_dev_inst->name() == "DSCope"); if (isDSCope) { trig_value = delta * 255.0 + 0.5; _trig_vpos = min(max(trig_value/255.0, 0+TrigMargin), 1-TrigMargin); @@ -781,7 +781,7 @@ QString DsoSignal::get_ms_string(int index) const void DsoSignal::update_zeroPos() { - if (strcmp(_dev_inst->dev_inst()->driver->name, "DSCope") == 0) { + if (_dev_inst->name() == "DSCope") { //double vpos_off = (0.5 - _zeroPos) * _vDial->get_value() * DS_CONF_DSO_VDIVS; double vpos_off = (0.5 - (get_zeroPos() - UpMargin) * 1.0/get_view_rect().height()) * _vDial->get_value() * DS_CONF_DSO_VDIVS; _dev_inst->set_config(_probe, NULL, SR_CONF_VPOS, @@ -888,7 +888,7 @@ void DsoSignal::paint_mid(QPainter &p, int left, int right) return; const uint16_t number_channels = snapshot->get_channel_num(); - if ((strcmp(_dev_inst->dev_inst()->driver->name, "DSLogic") == 0) && + if (_dev_inst->name() == "DSLogic" && (unsigned int)get_index() >= number_channels) return; @@ -999,7 +999,7 @@ void DsoSignal::paint_trace(QPainter &p, float top = get_view_rect().top(); float bottom = get_view_rect().bottom(); float zeroP = _zeroPos * get_view_rect().height() + top;; - if (strcmp(_dev_inst->dev_inst()->driver->name, "DSCope") == 0 && + if (_dev_inst->name() == "DSCope" && _view->session().get_capture_state() == SigSession::Running) _zero_off = _zeroPos * 255; float x = (start / samples_per_pixel - pixels_offset) + left; @@ -1055,7 +1055,7 @@ void DsoSignal::paint_envelope(QPainter &p, float top = get_view_rect().top(); float bottom = get_view_rect().bottom(); float zeroP = _zeroPos * get_view_rect().height() + top; - if (strcmp(_dev_inst->dev_inst()->driver->name, "DSCope") == 0 && + if (_dev_inst->name() == "DSCope" && _view->session().get_capture_state() == SigSession::Running) _zero_off = _zeroPos * 255; for(uint64_t sample = 0; sample < e.length-1; sample++) { @@ -1172,7 +1172,7 @@ bool DsoSignal::mouse_press(int right, const QPoint pt) const QRectF x100_rect = get_rect(DSO_X100, y, right); if (chEn_rect.contains(pt)) { - if (strcmp(_dev_inst->dev_inst()->driver->name, "virtual-session") && + if (_dev_inst->name() != "virtual-session" && !_view->session().get_data_lock()) set_enable(!enabled()); return true; @@ -1197,9 +1197,9 @@ bool DsoSignal::mouse_press(int right, const QPoint pt) setted = true; } } - } else if (strcmp(_dev_inst->dev_inst()->driver->name, "virtual-session") && + } else if (_dev_inst->name() != "virtual-session" && acdc_rect.contains(pt)) { - if (strcmp(_view->session().get_device()->dev_inst()->driver->name, "DSLogic") == 0) + if (_dev_inst->name() == "DSLogic") set_acCoupling((get_acCoupling()+1)%2); else set_acCoupling((get_acCoupling()+1)%2); diff --git a/DSView/pv/view/view.cpp b/DSView/pv/view/view.cpp index a85eb321..f35d7a2e 100644 --- a/DSView/pv/view/view.cpp +++ b/DSView/pv/view/view.cpp @@ -73,7 +73,7 @@ View::View(SigSession &session, pv::toolbars::SamplingBar *sampling_bar, QWidget QScrollArea(parent), _session(session), _sampling_bar(sampling_bar), - _scale(1e-8), + _scale(1e-5), _preScale(1e-6), _maxscale(1e9), _minscale(1e-15), @@ -81,7 +81,6 @@ View::View(SigSession &session, pv::toolbars::SamplingBar *sampling_bar, QWidget _preOffset(0), _updating_scroll(false), _show_cursors(false), - _trig_pos(0), _hover_point(-1, -1) { setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn); @@ -136,12 +135,13 @@ View::View(SigSession &session, pv::toolbars::SamplingBar *sampling_bar, QWidget _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); - QWidget* bottom = new QWidget(this); - bottom->setFixedHeight(10); - layout->addWidget(bottom, 1, 0); + _viewbottom = new widgets::ViewStatus(this); + _viewbottom->setFixedHeight(StatusHeight); + layout->addWidget(_viewbottom, 1, 0); setViewport(_viewcenter); connect(_vsplitter, SIGNAL(splitterMoved(int,int)), this, SLOT(splitterMoved(int, int))); @@ -152,8 +152,14 @@ View::View(SigSession &session, pv::toolbars::SamplingBar *sampling_bar, QWidget this, SLOT(signals_changed()), Qt::DirectConnection); connect(&_session, SIGNAL(data_updated()), this, SLOT(data_updated())); + connect(&_session, SIGNAL(receive_header()), + this, SLOT(receive_header())); connect(&_session, SIGNAL(receive_trigger(quint64)), this, SLOT(set_trig_pos(quint64))); + connect(&_session, SIGNAL(frame_ended()), + this, SLOT(receive_end())); + connect(&_session, SIGNAL(frame_began()), + this, SLOT(frame_began())); connect(&_session, SIGNAL(show_region(uint64_t,uint64_t)), this, SLOT(show_region(uint64_t, uint64_t))); @@ -321,27 +327,6 @@ void View::set_preScale_preOffset() vector< boost::shared_ptr > View::get_traces(int type) { -// const vector< boost::shared_ptr > sigs(_session.get_signals()); -// const vector< boost::shared_ptr > groups(_session.get_group_signals()); -//#ifdef ENABLE_DECODE -// const vector< boost::shared_ptr > decode_sigs( -// _session.get_decode_signals()); -// vector< boost::shared_ptr > traces( -// sigs.size() + groups.size() + decode_sigs.size()); -//#else -// vector< boost::shared_ptr > traces(sigs.size() + groups.size()); -//#endif - -// vector< boost::shared_ptr >::iterator i = traces.begin(); -// i = copy(sigs.begin(), sigs.end(), i); -//#ifdef ENABLE_DECODE -// i = copy(decode_sigs.begin(), decode_sigs.end(), i); -//#endif -// i = copy(groups.begin(), groups.end(), i); - -// stable_sort(traces.begin(), traces.end(), compare_trace_v_offsets); -// return traces; - const vector< boost::shared_ptr > sigs(_session.get_signals()); const vector< boost::shared_ptr > groups(_session.get_group_signals()); #ifdef ENABLE_DECODE @@ -422,20 +407,52 @@ void View::show_search_cursor(bool show) viewport_update(); } +void View::status_clear() +{ + _viewbottom->clear(); +} + +void View::receive_header() +{ + status_clear(); +} + +void View::frame_began() +{ + if (_session.get_device()->dev_inst()->mode == LOGIC) + _viewbottom->set_trig_time(_session.get_trigger_time()); +} + +void View::receive_end() +{ + if (_session.get_device()->dev_inst()->mode == LOGIC) { + GVariant *gvar = _session.get_device()->get_config(NULL, NULL, SR_CONF_RLE); + if (gvar != NULL) { + bool rle = g_variant_get_boolean(gvar); + g_variant_unref(gvar); + if (rle) { + gvar = _session.get_device()->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); + } + } + } + } + } +} + void View::set_trig_pos(quint64 trig_pos) { const double time = trig_pos * 1.0 / _session.cur_samplerate(); - _trig_pos = trig_pos; _trig_cursor->set_index(trig_pos); - if (ds_trigger_get_en()) { + if (ds_trigger_get_en() || _session.get_device()->name() == "virtual-session") { _show_trig_cursor = true; set_scale_offset(_scale, time - _scale * get_view_width() / 2); } - _trigger_time = QDateTime::currentDateTime(); - const int64_t secs = time - _session.get_device()->get_sample_time(); - _trigger_time = _trigger_time.addSecs(secs); - _ruler->update(); viewport_update(); } @@ -452,11 +469,6 @@ void View::set_search_pos(uint64_t search_pos) viewport_update(); } -uint64_t View::get_trig_pos() -{ - return _trig_pos; -} - uint64_t View::get_search_pos() { return _search_pos; @@ -555,7 +567,7 @@ void View::update_scale_offset() _preScale = _scale; _preOffset = _offset; - _trig_cursor->set_index(_trig_pos); + _trig_cursor->set_index(_session.get_trigger_pos()); _ruler->update(); viewport_update(); @@ -611,7 +623,6 @@ void View::signals_changed() } const double height = (_time_viewport->height() - - horizontalScrollBar()->height() - 2 * SignalMargin * time_traces.size()) * 1.0 / total_rows; if (_session.get_device()->dev_inst()->mode == LOGIC) { @@ -998,11 +1009,6 @@ void View::update_calibration() } } -QString View::trigger_time() -{ - return _trigger_time.toString("yyyy-MM-dd hh:mm:ss ddd"); -} - void View::show_region(uint64_t start, uint64_t end) { assert(start <= end); diff --git a/DSView/pv/view/view.h b/DSView/pv/view/view.h index 55f8ae91..af6f9337 100644 --- a/DSView/pv/view/view.h +++ b/DSView/pv/view/view.h @@ -43,6 +43,7 @@ #include "../view/viewport.h" #include "cursor.h" #include "signal.h" +#include "../widgets/viewstatus.h" namespace pv { @@ -83,6 +84,8 @@ public: static constexpr double MaxViewRate = 1.0; static const int MaxPixelsPerSample = 100; + static const int StatusHeight = 25; + public: explicit View(SigSession &session, pv::toolbars::SamplingBar *sampling_bar, QWidget *parent = 0); @@ -158,7 +161,6 @@ public: //void set_trig_pos(uint64_t trig_pos); void set_search_pos(uint64_t search_pos); - uint64_t get_trig_pos(); uint64_t get_search_pos(); /* @@ -187,8 +189,6 @@ public: void set_sample_limit(uint64_t sample_limit, bool force = false); - QString trigger_time(); - QString get_measure(QString option); void viewport_update(); @@ -233,6 +233,7 @@ public slots: void show_region(uint64_t start, uint64_t end); // -- calibration void update_calibration(); + void status_clear(); private slots: @@ -245,8 +246,14 @@ private slots: void header_updated(); + void receive_header(); + void set_trig_pos(quint64 trig_pos); + void receive_end(); + + void frame_began(); + // calibration for oscilloscope void show_calibration(); void hide_calibration(); @@ -260,6 +267,7 @@ private: pv::toolbars::SamplingBar *_sampling_bar; QWidget *_viewcenter; + widgets::ViewStatus *_viewbottom; QSplitter *_vsplitter; Viewport * _time_viewport; Viewport * _fft_viewport; @@ -291,14 +299,12 @@ private: Cursor *_trig_cursor; bool _show_trig_cursor; - uint64_t _trig_pos; Cursor *_search_cursor; bool _show_search_cursor; uint64_t _search_pos; QPointF _hover_point; dialogs::Calibration *_cali; - QDateTime _trigger_time; }; } // namespace view diff --git a/DSView/pv/view/viewport.cpp b/DSView/pv/view/viewport.cpp index 199d35cb..38f0c117 100644 --- a/DSView/pv/view/viewport.cpp +++ b/DSView/pv/view/viewport.cpp @@ -172,8 +172,6 @@ void Viewport::paintEvent(QPaintEvent *event) if (_view.get_signalHeight() != _curSignalHeight) _curSignalHeight = _view.get_signalHeight(); - paintTrigTime(p); - p.end(); } @@ -379,7 +377,7 @@ void Viewport::mousePressEvent(QMouseEvent *event) _mouse_down_point = event->pos(); _mouse_down_offset = _view.offset(); _drag_strength = 0; - _time.start(); + _time.restart(); if (event->button() == Qt::LeftButton) { const vector< boost::shared_ptr > sigs(_view.session().get_signals()); @@ -456,13 +454,60 @@ void Viewport::mouseMoveEvent(QMouseEvent *event) uint64_t sample_rate = _view.session().cur_samplerate(); TimeMarker* grabbed_marker = _view.get_ruler()->get_grabbed_cursor(); if (_view.cursors_shown() && grabbed_marker) { - const double cur_time = _view.offset() + _view.hover_point().x() * _view.scale(); + double curX = _view.hover_point().x(); + uint64_t index0 = 0, index1 = 0, index2 = 0; + bool logic = false; + const vector< boost::shared_ptr > sigs(_view.session().get_signals()); + BOOST_FOREACH(const boost::shared_ptr s, sigs) { + assert(s); + boost::shared_ptr logicSig; + boost::shared_ptr dsoSig; + if ((_view.session().get_device()->dev_inst()->mode == LOGIC) && + (logicSig = dynamic_pointer_cast(s))) { + if (logicSig->measure(event->pos(), index0, index1, index2)) { + logic = true; + break; + } + } + if ((_view.session().get_device()->dev_inst()->mode == DSO) && + (dsoSig = dynamic_pointer_cast(s))) { + curX = min(dsoSig->get_view_rect().right(), curX); + break; + } + } + + const double cur_time = _view.offset() + curX * _view.scale(); const double pos = cur_time * sample_rate; const double pos_delta = pos - (uint64_t)pos; - if ( pos_delta < 0.5) + const double samples_per_pixel = sample_rate * _view.scale(); + const double index_offset = _view.offset() / _view.scale(); + const double curP = index0 / samples_per_pixel - index_offset; + const double curN = index1 / samples_per_pixel - index_offset; + if (logic && (curX - curP < SnapMinSpace || curN - curX < SnapMinSpace)) { + if (curX - curP < curN - curX) + grabbed_marker->set_index(index0); + else + grabbed_marker->set_index(index1); + } else if ( pos_delta < 0.5) { grabbed_marker->set_index((uint64_t)floor(pos)); - else + } else { grabbed_marker->set_index((uint64_t)ceil(pos)); + } + } + } + + if (_action_type == DSO_XM_STEP1 || _action_type == DSO_XM_STEP2) { + BOOST_FOREACH(const boost::shared_ptr s, _view.session().get_signals()) { + assert(s); + if (!s->get_view_rect().contains(event->pos())) { + _dso_xm_valid = false; + _mm_width = "#####"; + _mm_period = "#####"; + _mm_freq = "#####"; + _mm_duty = "#####"; + _action_type = NO_ACTION; + } + break; } } @@ -510,7 +555,6 @@ void Viewport::mouseReleaseEvent(QMouseEvent *event) if (_time.elapsed() < 200 && abs(_drag_strength) < MinorDragOffsetUp && abs(strength) > MinorDragRateUp) { - _drag_strength = _drag_strength; _drag_timer.start(DragTimerInterval); _action_type = LOGIC_MOVE; } else if (_time.elapsed() < 200 && @@ -609,10 +653,14 @@ void Viewport::mouseReleaseEvent(QMouseEvent *event) const double scale = _view.scale(); const double samples_per_pixel = sample_rate * scale; _dso_xm_index[2] = event->pos().x() * samples_per_pixel + _view.offset() * sample_rate; - const uint64_t max_index = max(_dso_xm_index[1], _dso_xm_index[2]); + uint64_t max_index = max(_dso_xm_index[1], _dso_xm_index[2]); _dso_xm_index[1] = min(_dso_xm_index[1], _dso_xm_index[2]); _dso_xm_index[2] = max_index; + max_index = max(_dso_xm_index[0], _dso_xm_index[1]); + _dso_xm_index[0] = min(_dso_xm_index[0], _dso_xm_index[1]); + _dso_xm_index[1] = max_index; + _action_type = NO_ACTION; } else if (event->button() == Qt::RightButton) { _action_type = NO_ACTION; @@ -639,9 +687,22 @@ void Viewport::mouseReleaseEvent(QMouseEvent *event) _edge_rising = 0; _edge_falling = 0; } else if (_action_type == LOGIC_MOVE) { - _drag_strength = 0; - _drag_timer.stop(); - _action_type = NO_ACTION; + if (_mouse_down_point == event->pos()) { + _drag_strength = 0; + _drag_timer.stop(); + _action_type = NO_ACTION; + } else { + const double strength = _drag_strength*DragTimerInterval*1.0/_time.elapsed(); + if (_time.elapsed() < 200 && + abs(_drag_strength) < MinorDragOffsetUp && + abs(strength) > MinorDragRateUp) { + _drag_timer.start(DragTimerInterval); + } else if (_time.elapsed() < 200 && + abs(strength) > DragTimerInterval) { + _drag_strength = strength * 5; + _drag_timer.start(DragTimerInterval); + } + } } else if (_action_type == LOGIC_ZOOM) { if (event->pos().x() != _mouse_down_point.x()) { const double newOffset = _view.offset() + (min(event->pos().x(), _mouse_down_point.x()) + 0.5) * _view.scale(); @@ -686,13 +747,19 @@ void Viewport::mouseDoubleClickEvent(QMouseEvent *event) _mm_duty = "#####"; measure_updated(); } else if (_action_type == NO_ACTION) { - uint64_t sample_rate = _view.session().cur_samplerate(); - double scale = _view.scale(); - const double samples_per_pixel = sample_rate * scale; - _dso_xm_index[0] = event->pos().x() * samples_per_pixel + - _view.offset() * sample_rate; - _dso_xm_y = event->pos().y(); - _action_type = DSO_XM_STEP0; + BOOST_FOREACH(const boost::shared_ptr s, _view.session().get_signals()) { + assert(s); + if (s->get_view_rect().contains(event->pos())) { + uint64_t sample_rate = _view.session().cur_samplerate(); + double scale = _view.scale(); + const double samples_per_pixel = sample_rate * scale; + _dso_xm_index[0] = event->pos().x() * samples_per_pixel + + _view.offset() * sample_rate; + _dso_xm_y = event->pos().y(); + _action_type = DSO_XM_STEP0; + } + break; + } } } } @@ -730,36 +797,25 @@ void Viewport::leaveEvent(QEvent *) _mouse_point = QPoint(-1, -1); //_view.show_cursors(false); - if (_action_type == CURS_MOVE) { - if (_view.cursors_shown()) { - list::iterator i = _view.get_cursorList().begin(); - while (i != _view.get_cursorList().end()) { - if ((*i)->grabbed()) { - _view.get_ruler()->rel_grabbed_cursor(); - } - i++; - } - } - } else if (_action_type == LOGIC_EDGE) { + if (_action_type == LOGIC_EDGE) { _edge_rising = 0; _edge_falling = 0; + _action_type = NO_ACTION; } else if (_action_type == LOGIC_MOVE) { _drag_strength = 0; _drag_timer.stop(); - } else if (_action_type == DSO_TRIG_MOVE) { - _drag_sig.reset(); + _action_type = NO_ACTION; } else if (_action_type == DSO_XM_STEP1 || _action_type == DSO_XM_STEP2) { _dso_xm_valid = false; - _mm_width = "#####"; - _mm_period = "#####"; - _mm_freq = "#####"; - _mm_duty = "#####"; + _mm_width = "#####"; + _mm_period = "#####"; + _mm_freq = "#####"; + _mm_duty = "#####"; + _action_type = NO_ACTION; } else if (_action_type == DSO_YM) { _dso_ym_valid = false; - } - - if (_action_type != NO_ACTION) _action_type = NO_ACTION; + } measure(); update(); @@ -1212,20 +1268,12 @@ void Viewport::on_drag_timer() _drag_strength /= DragDamping; if (_drag_strength != 0) _drag_timer.start(DragTimerInterval); - } else { + } else if (_action_type == NO_ACTION){ + _drag_strength = 0; _drag_timer.stop(); } } -void Viewport::paintTrigTime(QPainter &p) -{ - if (_view.session().get_device()->dev_inst()->mode == LOGIC) { - p.setPen(Trace::DARK_FORE); - p.drawText(this->rect(), Qt::AlignRight | Qt::AlignBottom, - "Last Trigger Time: "+_view.trigger_time()); - } -} - void Viewport::set_need_update(bool update) { _need_update = update; diff --git a/DSView/pv/view/viewport.h b/DSView/pv/view/viewport.h index cee1e9eb..9afef47b 100644 --- a/DSView/pv/view/viewport.h +++ b/DSView/pv/view/viewport.h @@ -58,6 +58,7 @@ public: static const int DsoMeasureStages = 3; static const double MinorDragRateUp; static const double DragDamping; + static const int SnapMinSpace = 10; enum ActionType { NO_ACTION, @@ -70,11 +71,8 @@ public: DSO_XM_STEP0, DSO_XM_STEP1, DSO_XM_STEP2, - DSO_XM_STEP3, DSO_YM, - DSO_TRIG_MOVE, - - DECODE_REGION + DSO_TRIG_MOVE }; enum MeasureType { @@ -116,7 +114,6 @@ private: void paintSignals(QPainter& p); void paintProgress(QPainter& p); void paintMeasure(QPainter &p); - void paintTrigTime(QPainter &p); void measure(); diff --git a/DSView/pv/widgets/viewstatus.cpp b/DSView/pv/widgets/viewstatus.cpp new file mode 100644 index 00000000..2b57a6c9 --- /dev/null +++ b/DSView/pv/widgets/viewstatus.cpp @@ -0,0 +1,66 @@ +/* + * This file is part of the DSView project. + * DSView is based on PulseView. + * + * Copyright (C) 2016 DreamSourceLab + * + * 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 "viewstatus.h" + +#include +#include +#include + +#include "../view/trace.h" + +namespace pv { +namespace widgets { + +ViewStatus::ViewStatus(QWidget *parent) : QWidget(parent) +{ +} + +void ViewStatus::paintEvent(QPaintEvent *) +{ + QStyleOption opt; + opt.init(this); + QPainter p(this); + style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); + + p.setPen(pv::view::Trace::DARK_FORE); + p.drawText(this->rect(), Qt::AlignLeft | Qt::AlignVCenter, _rle_depth); + p.drawText(this->rect(), Qt::AlignRight | Qt::AlignVCenter, _trig_time); +} + +void ViewStatus::clear() +{ + _trig_time.clear(); + _rle_depth.clear(); +} + +void ViewStatus::set_trig_time(QDateTime time) +{ + _trig_time = tr("Trigger Time: ") + time.toString("yyyy-MM-dd hh:mm:ss ddd"); +} + +void ViewStatus::set_rle_depth(uint64_t depth) +{ + _rle_depth = tr("RLE FULL: ") + QString::number(depth) + tr(" Samples Captured!"); +} + +} // namespace widgets +} // namespace pv diff --git a/DSView/pv/widgets/viewstatus.h b/DSView/pv/widgets/viewstatus.h new file mode 100644 index 00000000..58078d0d --- /dev/null +++ b/DSView/pv/widgets/viewstatus.h @@ -0,0 +1,58 @@ +/* + * This file is part of the DSView project. + * DSView is based on PulseView. + * + * Copyright (C) 2016 DreamSourceLab + * + * 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 + */ + +#ifndef DSVIEW_PV_WIDGETS_VIEWSTATUS_H +#define DSVIEW_PV_WIDGETS_VIEWSTATUS_H + +#include +#include +#include "QDateTime" + +namespace pv { + +class SigSession; + +namespace widgets { + +class ViewStatus : public QWidget +{ + Q_OBJECT +public: + explicit ViewStatus(QWidget *parent = 0); + + void paintEvent(QPaintEvent *); + +signals: + +public slots: + void clear(); + void set_trig_time(QDateTime time); + void set_rle_depth(uint64_t depth); + +private: + QString _trig_time; + QString _rle_depth; +}; + +} // namespace widgets +} // namespace pv + +#endif // DSVIEW_PV_WIDGETS_VIEWSTATUS_H diff --git a/libsigrok4DSL/hardware/DSL/dscope.c b/libsigrok4DSL/hardware/DSL/dscope.c index 30c0549d..83928db4 100644 --- a/libsigrok4DSL/hardware/DSL/dscope.c +++ b/libsigrok4DSL/hardware/DSL/dscope.c @@ -850,10 +850,10 @@ static GSList *scan(GSList *options) sdi->conn = sr_usb_dev_inst_new(libusb_get_bus_number(devlist[i]), libusb_get_device_address(devlist[i]), NULL); } else { - char *firmware = malloc(strlen(config_path)+strlen(prof->firmware)+1); + char *firmware = malloc(strlen(DS_RES_PATH)+strlen(prof->firmware)+1); if (firmware == NULL) return NULL; - strcpy(firmware, config_path); + strcpy(firmware, DS_RES_PATH); strcat(firmware, prof->firmware); if (ezusb_upload_firmware(devlist[i], USB_CONFIGURATION, firmware) == SR_OK) @@ -1452,12 +1452,12 @@ static int config_set(int id, GVariant *data, struct sr_dev_inst *sdi, /* Takes >= 10ms for the FX2 to be ready for FPGA configure. */ g_usleep(10 * 1000); //char filename[256]; - //sprintf(filename,"%s%s",config_path,devc->profile->fpga_bit33); + //sprintf(filename,"%s%s",DS_RES_PATH,devc->profile->fpga_bit33); //const char *fpga_bit = filename; - char *fpga_bit = malloc(strlen(config_path)+strlen(devc->profile->fpga_bit33)+1); + char *fpga_bit = malloc(strlen(DS_RES_PATH)+strlen(devc->profile->fpga_bit33)+1); if (fpga_bit == NULL) return SR_ERR_MALLOC; - strcpy(fpga_bit, config_path); + strcpy(fpga_bit, DS_RES_PATH); strcat(fpga_bit, devc->profile->fpga_bit33); ret = fpga_config(usb->devhdl, fpga_bit); if (ret != SR_OK) { @@ -2102,10 +2102,10 @@ static int dev_open(struct sr_dev_inst *sdi) } else { /* Takes >= 10ms for the FX2 to be ready for FPGA configure. */ g_usleep(10 * 1000); - char *fpga_bit = malloc(strlen(config_path)+strlen(devc->profile->fpga_bit33)+1); + char *fpga_bit = malloc(strlen(DS_RES_PATH)+strlen(devc->profile->fpga_bit33)+1); if (fpga_bit == NULL) return SR_ERR_MALLOC; - strcpy(fpga_bit, config_path); + strcpy(fpga_bit, DS_RES_PATH); strcat(fpga_bit, devc->profile->fpga_bit33); ret = fpga_config(usb->devhdl, fpga_bit); if (ret != SR_OK) { @@ -2172,10 +2172,6 @@ static void abort_acquisition(struct DSL_context *devc) else sr_info("Stop DSCope acquisition!"); - ret = command_dso_ctrl(usb->devhdl, dso_cmd_gen((struct sr_dev_inst *)devc->cb_data, NULL, SR_CONF_ZERO_OVER)); - if (ret != SR_OK) - sr_err("DSO zero over command failed!"); - /* Cancel exist transfers */ if (devc->num_transfers) for (i = devc->num_transfers - 1; i >= 0; i--) { @@ -2432,10 +2428,6 @@ static void receive_transfer(struct libusb_transfer *transfer) mstatus.vlen = instant_buffer_size; } - if (devc->zero) { - dso_zero(sdi, mstatus); - } - const uint32_t divider = devc->zero ? 0x1 : (uint32_t)ceil(DSCOPE_MAX_SAMPLERATE * 1.0 / devc->cur_samplerate / channel_en_cnt); if ((mstatus.sample_divider == divider && mstatus.vlen != 0 && @@ -2675,6 +2667,10 @@ static int receive_data(int fd, int revents, const struct sr_dev_inst *sdi) tv.tv_sec = tv.tv_usec = 0; libusb_handle_events_timeout_completed(drvc->sr_ctx->libusb_ctx, &tv, &completed); + if (devc->zero) { + dso_zero(sdi, mstatus); + } + return TRUE; } diff --git a/libsigrok4DSL/hardware/DSL/dslogic.c b/libsigrok4DSL/hardware/DSL/dslogic.c index e99f92ce..842ed953 100644 --- a/libsigrok4DSL/hardware/DSL/dslogic.c +++ b/libsigrok4DSL/hardware/DSL/dslogic.c @@ -700,7 +700,7 @@ static struct DSL_context *DSLogic_dev_new(void) devc->stream = FALSE; devc->mstatus_valid = FALSE; devc->data_lock = FALSE; - devc->max_height = 1; + devc->max_height = 0; return devc; } @@ -855,11 +855,11 @@ static GSList *scan(GSList *options) libusb_get_device_address(devlist[i]), NULL); } else { char *firmware; - if (!(firmware = g_try_malloc(strlen(config_path)+strlen(prof->firmware)+1))) { + if (!(firmware = g_try_malloc(strlen(DS_RES_PATH)+strlen(prof->firmware)+1))) { sr_err("Firmware path malloc error!"); return NULL; } - strcpy(firmware, config_path); + strcpy(firmware, DS_RES_PATH); strcat(firmware, prof->firmware); if (ezusb_upload_firmware(devlist[i], USB_CONFIGURATION, firmware) == SR_OK) @@ -1326,25 +1326,6 @@ static int config_set(int id, GVariant *data, struct sr_dev_inst *sdi, sr_dbg("%s: setting mode to %d", __func__, sdi->mode); if (sdi->mode == DSO) { GList *l; - for(l = sdi->channels; l; l = l->next) { - struct sr_channel *probe = (struct sr_channel *)l->data; - zero_info.zero_addr = (zero_base_addr + probe->index * sizeof(struct cmd_zero_info)); - if ((ret = command_rd_nvm(usb->devhdl, (unsigned char *)&zero_info, zero_info.zero_addr, sizeof(struct cmd_zero_info))) != SR_OK) { - sr_err("Send Get Zero command failed!"); - } else { - if (zero_info.zero_addr == (zero_base_addr + probe->index * sizeof(struct cmd_zero_info))) { - ret = command_dso_ctrl(usb->devhdl, dso_cmd_gen(sdi, probe, SR_CONF_ZERO_SET)); - if (ret != SR_OK) { - sr_err("Set Zero command failed!"); - return ret; - } - } else { - devc->zero = TRUE; - sr_info("Zero have not been setted!"); - } - } - } - for(l = sdi->channels; l; l = l->next) { struct sr_channel *probe = (struct sr_channel *)l->data; ret = command_dso_ctrl(usb->devhdl, dso_cmd_gen(sdi, probe, SR_CONF_COUPLING)); @@ -1488,11 +1469,11 @@ static int config_set(int id, GVariant *data, struct sr_dev_inst *sdi, /* Takes >= 10ms for the FX2 to be ready for FPGA configure. */ g_usleep(10 * 1000); char *fpga_bit; - if (!(fpga_bit = g_try_malloc(strlen(config_path)+strlen(devc->profile->fpga_bit33)+1))) { + if (!(fpga_bit = g_try_malloc(strlen(DS_RES_PATH)+strlen(devc->profile->fpga_bit33)+1))) { sr_err("fpag_bit path malloc error!"); return SR_ERR_MALLOC; } - strcpy(fpga_bit, config_path); + strcpy(fpga_bit, DS_RES_PATH); switch(devc->th_level) { case SR_TH_3V3: strcat(fpga_bit, devc->profile->fpga_bit33);; @@ -1846,11 +1827,11 @@ static int dev_open(struct sr_dev_inst *sdi) /* Takes >= 10ms for the FX2 to be ready for FPGA configure. */ g_usleep(10 * 1000); char *fpga_bit; - if (!(fpga_bit = g_try_malloc(strlen(config_path)+strlen(devc->profile->fpga_bit33)+1))) { + if (!(fpga_bit = g_try_malloc(strlen(DS_RES_PATH)+strlen(devc->profile->fpga_bit33)+1))) { sr_err("fpag_bit path malloc error!"); return SR_ERR_MALLOC; } - strcpy(fpga_bit, config_path); + strcpy(fpga_bit, DS_RES_PATH); switch(devc->th_level) { case SR_TH_3V3: strcat(fpga_bit, devc->profile->fpga_bit33);; @@ -1955,10 +1936,6 @@ static void abort_acquisition(struct DSL_context *devc) else sr_info("Stop DSLogic acquisition!"); - ret = command_dso_ctrl(usb->devhdl, dso_cmd_gen((struct sr_dev_inst *)devc->cb_data, NULL, SR_CONF_ZERO_OVER)); - if (ret != SR_OK) - sr_err("DSO zero over command failed!"); - /* Cancel exist transfers */ if (devc->num_transfers) for (i = devc->num_transfers - 1; i >= 0; i--) { @@ -2439,9 +2416,12 @@ static void receive_trigger_pos(struct libusb_transfer *transfer) struct sr_datafeed_dso dso; struct sr_datafeed_analog analog; struct ds_trigger_pos *trigger_pos; + const struct sr_dev_inst *sdi; int ret; devc = transfer->user_data; + sdi = devc->cb_data; + sr_info("receive_trigger_pos(): status %d; timeout %d; received %d bytes.", transfer->status, transfer->timeout, transfer->actual_length); @@ -2454,13 +2434,13 @@ static void receive_trigger_pos(struct libusb_transfer *transfer) switch (transfer->status) { case LIBUSB_TRANSFER_COMPLETED: if (transfer->actual_length == sizeof(struct ds_trigger_pos)) { - if (devc->stream || trigger_pos->remain_cnt < devc->limit_samples) { - if (!devc->stream) + if (sdi->mode != LOGIC || devc->stream || trigger_pos->remain_cnt < devc->limit_samples) { + if (sdi->mode == LOGIC && !devc->stream) devc->actual_samples = (devc->limit_samples - ceil(devc->cur_samplerate * 1.0 / DSLOGIC_MAX_LOGIC_SAMPLERATE) * (trigger_pos->remain_cnt)); packet.type = SR_DF_TRIGGER; packet.payload = trigger_pos; - sr_session_send(devc->cb_data, &packet); + sr_session_send(sdi, &packet); devc->status = DSL_TRIGGERED; free_transfer(transfer); @@ -2500,7 +2480,7 @@ static void receive_trigger_pos(struct libusb_transfer *transfer) } if (devc->status == DSL_TRIGGERED) { - if ((ret = dev_transfer_start(devc->cb_data)) != SR_OK) { + if ((ret = dev_transfer_start(sdi)) != SR_OK) { sr_err("%s: could not start data transfer" "(%d)%d", __func__, ret, errno); } diff --git a/libsigrok4DSL/hardware/demo/demo.c b/libsigrok4DSL/hardware/demo/demo.c index e26036c6..60e62a8c 100644 --- a/libsigrok4DSL/hardware/demo/demo.c +++ b/libsigrok4DSL/hardware/demo/demo.c @@ -119,6 +119,8 @@ struct dev_context { uint16_t trigger_mask; uint16_t trigger_value; uint16_t trigger_edge; + uint8_t trigger_slope; + uint8_t trigger_source; }; static const int hwcaps[] = { @@ -342,7 +344,7 @@ static GSList *hw_scan(GSList *options) devc->sample_generator = PATTERN_SINE; devc->timebase = 200; devc->data_lock = FALSE; - devc->max_height = 1; + devc->max_height = 0; sdi->priv = devc; @@ -690,7 +692,17 @@ static int config_set(int id, GVariant *data, struct sr_dev_inst *sdi, sr_dbg("%s: setting AC COUPLING of channel %d to %d", __func__, ch->index, ch->coupling); ret = SR_OK; - } else if (id == SR_CONF_TRIGGER_VALUE) { + } else if (id == SR_CONF_TRIGGER_SOURCE) { + devc->trigger_source = g_variant_get_byte(data); + sr_dbg("%s: setting Trigger Source to %d", + __func__, devc->trigger_source); + ret = SR_OK; + } else if (id == SR_CONF_TRIGGER_SLOPE) { + devc->trigger_slope = g_variant_get_byte(data); + sr_dbg("%s: setting Trigger Slope to %d", + __func__, devc->trigger_slope); + ret = SR_OK; + } else if (id == SR_CONF_TRIGGER_VALUE) { ch->trig_value = g_variant_get_byte(data); sr_dbg("%s: setting channel %d Trigger Value to %d", __func__, ch->index, ch->trig_value); @@ -798,7 +810,7 @@ static void samples_generator(uint16_t *buf, uint64_t size, } else if (sdi->mode != DSO) { start_rand = rand()%len; for (i = 0; i < size; i++) { - index = (i/g_slist_length(sdi->channels)+start_rand)%len; + index = (i/10/g_slist_length(sdi->channels)+start_rand)%len; *(buf + i) = (uint16_t)(((const_dc+pre_buf[index]) << 8) + (const_dc+pre_buf[index])); } } else { diff --git a/libsigrok4DSL/libsigrok.h b/libsigrok4DSL/libsigrok.h index 319a35f5..72a022d9 100644 --- a/libsigrok4DSL/libsigrok.h +++ b/libsigrok4DSL/libsigrok.h @@ -111,6 +111,8 @@ enum { #define DS_CONF_DSO_HDIVS 10 #define DS_CONF_DSO_VDIVS 10 +#define DS_RES_PATH "/usr/local/share/DSView/res/" + /** libsigrok loglevels. */ enum { SR_LOG_NONE = 0, /**< Output no messages at all. */ @@ -863,6 +865,16 @@ enum { */ SR_CONF_LIMIT_SAMPLES, + /** + * Absolute time record for session driver + */ + SR_CONF_TRIGGER_TIME, + + /** + * Trigger position for session driver + */ + SR_CONF_TRIGGER_POS, + /** * The actual sample count received */ @@ -974,8 +986,6 @@ enum { SR_GND_COUPLING = 2, }; -extern char config_path[256]; - struct sr_dev_mode { char *name; int mode; diff --git a/libsigrok4DSL/proto.h b/libsigrok4DSL/proto.h index 854f445e..223f1ec0 100644 --- a/libsigrok4DSL/proto.h +++ b/libsigrok4DSL/proto.h @@ -111,7 +111,7 @@ SR_API int sr_session_start(void); SR_API int sr_session_run(void); SR_API int sr_session_stop(void); SR_API int sr_session_save(const char *filename, const struct sr_dev_inst *sdi, - unsigned char *buf, int unitsize, int units); + unsigned char *buf, int unitsize, uint64_t samples, int64_t trig_time, uint64_t trig_pos); SR_API int sr_session_save_init(const char *filename, uint64_t samplerate, char **channels); SR_API int sr_session_append(const char *filename, unsigned char *buf, diff --git a/libsigrok4DSL/session_driver.c b/libsigrok4DSL/session_driver.c index fa1d0c0f..d61435ff 100644 --- a/libsigrok4DSL/session_driver.c +++ b/libsigrok4DSL/session_driver.c @@ -52,6 +52,8 @@ struct session_vdev { int bytes_read; uint64_t samplerate; uint64_t total_samples; + int64_t trig_time; + uint64_t trig_pos; int unitsize; int num_probes; uint64_t timebase; @@ -171,6 +173,8 @@ static int dev_open(struct sr_dev_inst *sdi) sr_err("%s: vdev->buf malloc failed", __func__); return SR_ERR_MALLOC; } + vdev->trig_pos = 0; + vdev->trig_time = 0; dev_insts = g_slist_append(dev_insts, sdi); @@ -211,6 +215,13 @@ static int config_get(int id, GVariant **data, const struct sr_dev_inst *sdi, } else return SR_ERR; break; + case SR_CONF_TRIGGER_TIME: + if (sdi) { + vdev = sdi->priv; + *data = g_variant_new_int64(vdev->trig_time); + } else + return SR_ERR; + break; case SR_CONF_TIMEBASE: if (sdi) { vdev = sdi->priv; @@ -258,6 +269,9 @@ static int config_get(int id, GVariant **data, const struct sr_dev_inst *sdi, return SR_ERR; *data = g_variant_new_uint64(vdev->total_samples); break; + case SR_CONF_RLE_SAMPLELIMITS: + *data = g_variant_new_uint64(UINT64_MAX); + break; default: return SR_ERR_ARG; } @@ -299,7 +313,15 @@ static int config_set(int id, GVariant *data, const struct sr_dev_inst *sdi, samplecounts[0] = vdev->total_samples; sr_info("Setting limit samples to %" PRIu64 ".", vdev->total_samples); break; - case SR_CONF_CAPTURE_NUM_PROBES: + case SR_CONF_TRIGGER_TIME: + vdev->trig_time = g_variant_get_int64(data); + sr_info("Setting trigger time to %" PRId64 ".", vdev->trig_time); + break; + case SR_CONF_TRIGGER_POS: + vdev->trig_pos = g_variant_get_uint64(data); + sr_info("Setting trigger position to %" PRIu64 ".", vdev->trig_pos); + break; + case SR_CONF_CAPTURE_NUM_PROBES: vdev->num_probes = g_variant_get_uint64(data); break; case SR_CONF_EN_CH: @@ -407,6 +429,7 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi, { struct zip_stat zs; struct session_vdev *vdev; + struct sr_datafeed_packet packet; int ret; vdev = sdi->priv; @@ -435,6 +458,15 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi, /* Send header packet to the session bus. */ std_session_send_df_header(sdi, LOG_PREFIX); + /* Send trigger packet to the session bus */ + if (vdev->trig_pos != 0) { + struct ds_trigger_pos session_trigger; + session_trigger.real_pos = vdev->trig_pos; + packet.type = SR_DF_TRIGGER; + packet.payload = &session_trigger; + sr_session_send(sdi, &packet); + } + /* freewheeling source */ sr_session_source_add(-1, 0, 0, receive_data, sdi); diff --git a/libsigrok4DSL/session_file.c b/libsigrok4DSL/session_file.c index aa77128d..bf5b27c5 100644 --- a/libsigrok4DSL/session_file.c +++ b/libsigrok4DSL/session_file.c @@ -125,6 +125,7 @@ SR_API int sr_session_load(const char *filename) uint16_t probenum; uint64_t tmp_u64, total_probes, enabled_probes; uint16_t p; + int64_t tmp_64; char **sections, **keys, *metafile, *val, s[11]; char probename[SR_MAX_PROBENAME_LEN + 1]; int mode = LOGIC; @@ -205,10 +206,18 @@ SR_API int sr_session_load(const char *filename) tmp_u64 = strtoull(val, NULL, 10); sdi->driver->config_set(SR_CONF_LIMIT_SAMPLES, g_variant_new_uint64(tmp_u64), sdi, NULL, NULL); - } else if (!strcmp(keys[j], "hDiv")) { + } else if (!strcmp(keys[j], "hDiv")) { tmp_u64 = strtoull(val, NULL, 10); sdi->driver->config_set(SR_CONF_TIMEBASE, g_variant_new_uint64(tmp_u64), sdi, NULL, NULL); + } else if (!strcmp(keys[j], "trigger time")) { + tmp_64 = strtoll(val, NULL, 10); + sdi->driver->config_set(SR_CONF_TRIGGER_TIME, + g_variant_new_int64(tmp_64), sdi, NULL, NULL); + } else if (!strcmp(keys[j], "trigger pos")) { + tmp_u64 = strtoull(val, NULL, 10); + sdi->driver->config_set(SR_CONF_TRIGGER_POS, + g_variant_new_uint64(tmp_u64), sdi, NULL, NULL); } else if (!strcmp(keys[j], "total probes")) { total_probes = strtoull(val, NULL, 10); sdi->driver->config_set(SR_CONF_CAPTURE_NUM_PROBES, @@ -331,7 +340,7 @@ SR_API int sr_session_load(const char *filename) * upon other errors. */ SR_API int sr_session_save(const char *filename, const struct sr_dev_inst *sdi, - unsigned char *buf, int unitsize, int units) + unsigned char *buf, int unitsize, uint64_t samples, int64_t trig_time, uint64_t trig_pos) { GSList *l; GVariant *gvar; @@ -373,7 +382,7 @@ SR_API int sr_session_save(const char *filename, const struct sr_dev_inst *sdi, /* metadata */ fprintf(meta, "capturefile = data\n"); fprintf(meta, "unitsize = %d\n", unitsize); - fprintf(meta, "total samples = %d\n", units); + fprintf(meta, "total samples = %llu\n", samples); fprintf(meta, "total probes = %d\n", g_slist_length(sdi->channels)); if (sr_config_get(sdi->driver, sdi, NULL, NULL, SR_CONF_SAMPLERATE, &gvar) == SR_OK) { @@ -386,9 +395,13 @@ SR_API int sr_session_save(const char *filename, const struct sr_dev_inst *sdi, if (sdi->mode == DSO && sr_config_get(sdi->driver, sdi, NULL, NULL, SR_CONF_TIMEBASE, &gvar) == SR_OK) { timeBase = g_variant_get_uint64(gvar); - fprintf(meta, "hDiv = %d\n", timeBase); + fprintf(meta, "hDiv = %llu\n", timeBase); g_variant_unref(gvar); + } else if (sdi->mode == LOGIC) { + fprintf(meta, "trigger time = %lld\n", trig_time); } + fprintf(meta, "trigger pos = %llu\n", trig_pos); + probecnt = 1; for (l = sdi->channels; l; l = l->next) { probe = l->data; @@ -405,13 +418,13 @@ SR_API int sr_session_save(const char *filename, const struct sr_dev_inst *sdi, fprintf(meta, " vPos%d = %lf\n", probe->index, probe->vpos); if (sr_status_get(sdi, &status, 0, 0) == SR_OK) { if (probe->index == 0) { - fprintf(meta, " period%d = %d\n", probe->index, status.ch0_period); - fprintf(meta, " pcnt%d = %d\n", probe->index, status.ch0_pcnt); + fprintf(meta, " period%d = %llu\n", probe->index, status.ch0_period); + fprintf(meta, " pcnt%d = %lu\n", probe->index, status.ch0_pcnt); fprintf(meta, " max%d = %d\n", probe->index, status.ch0_max); fprintf(meta, " min%d = %d\n", probe->index, status.ch0_min); } else { - fprintf(meta, " period%d = %d\n", probe->index, status.ch1_period); - fprintf(meta, " pcnt%d = %d\n", probe->index, status.ch1_pcnt); + fprintf(meta, " period%d = %llu\n", probe->index, status.ch1_period); + fprintf(meta, " pcnt%d = %lu\n", probe->index, status.ch1_pcnt); fprintf(meta, " max%d = %d\n", probe->index, status.ch1_max); fprintf(meta, " min%d = %d\n", probe->index, status.ch1_min); } @@ -422,7 +435,7 @@ SR_API int sr_session_save(const char *filename, const struct sr_dev_inst *sdi, } if (!(logicsrc = zip_source_buffer(zipfile, buf, - units * unitsize, FALSE))) + samples * unitsize, FALSE))) return SR_ERR; snprintf(rawname, 15, "data"); if (zip_add(zipfile, rawname, logicsrc) == -1) @@ -544,7 +557,7 @@ SR_API int sr_session_save_init(const char *filename, uint64_t samplerate, * @param filename The name of the filename to append to. Must not be NULL. * @param buf The data to be appended. * @param unitsize The number of bytes per sample. - * @param units The number of samples. + * @param samples The number of samples. * * @retval SR_OK Success * @retval SR_ERR_ARG Invalid arguments From cace16dd8e0f2966a44937d19946bfca99e19482 Mon Sep 17 00:00:00 2001 From: DreamSourceLab Date: Tue, 21 Jun 2016 15:15:13 +0800 Subject: [PATCH 23/32] Make windows frameless and add own titlebar --- DSView/DSView.qrc | 4 + DSView/darkstyle/style.qss | 62 +++- DSView/icons/close.png | Bin 0 -> 173 bytes DSView/icons/maximize.png | Bin 0 -> 158 bytes DSView/icons/minimize.png | Bin 0 -> 108 bytes DSView/icons/restore.png | Bin 0 -> 238 bytes DSView/main.cpp | 16 +- DSView/pv/device/devinst.h | 1 + DSView/pv/devicemanager.cpp | 2 - DSView/pv/dialogs/about.cpp | 46 ++- DSView/pv/dialogs/about.h | 17 +- DSView/pv/dialogs/calibration.cpp | 22 +- DSView/pv/dialogs/calibration.h | 6 +- DSView/pv/dialogs/deviceoptions.cpp | 35 +- DSView/pv/dialogs/deviceoptions.h | 5 +- DSView/pv/dialogs/dsdialog.cpp | 144 ++++++++ DSView/pv/dialogs/dsdialog.h | 68 ++++ DSView/pv/dialogs/dsmessagebox.cpp | 129 ++++++++ .../{streamoptions.h => dsmessagebox.h} | 57 ++-- DSView/pv/dialogs/dsomeasure.cpp | 16 +- DSView/pv/dialogs/dsomeasure.h | 9 +- DSView/pv/dialogs/fftoptions.cpp | 10 +- DSView/pv/dialogs/fftoptions.h | 6 +- DSView/pv/dialogs/messagebox.cpp | 86 +++++ DSView/pv/dialogs/messagebox.h | 59 ++++ DSView/pv/dialogs/protocolexp.cpp | 11 +- DSView/pv/dialogs/protocolexp.h | 6 +- DSView/pv/dialogs/protocollist.cpp | 12 +- DSView/pv/dialogs/protocollist.h | 6 +- DSView/pv/dialogs/search.cpp | 25 +- DSView/pv/dialogs/search.h | 13 +- DSView/pv/dialogs/shadow.cpp | 104 ++++++ DSView/pv/dialogs/shadow.h | 59 ++++ DSView/pv/dialogs/storeprogress.cpp | 13 +- DSView/pv/dialogs/storeprogress.h | 3 + DSView/pv/dialogs/streamoptions.cpp | 106 ------ DSView/pv/dialogs/waitingdialog.cpp | 40 ++- DSView/pv/dialogs/waitingdialog.h | 8 +- DSView/pv/dock/dsotriggerdock.cpp | 42 +-- DSView/pv/dock/protocoldock.cpp | 22 +- DSView/pv/dock/searchdock.cpp | 64 ++-- DSView/pv/dock/triggerdock.cpp | 22 +- DSView/pv/mainframe.cpp | 307 ++++++++++++++++++ DSView/pv/mainframe.h | 103 ++++++ DSView/pv/mainwindow.cpp | 90 ++--- DSView/pv/mainwindow.h | 4 +- DSView/pv/sigsession.cpp | 23 +- DSView/pv/sigsession.h | 2 +- DSView/pv/toolbars/filebar.cpp | 46 ++- DSView/pv/toolbars/logobar.cpp | 12 +- DSView/pv/toolbars/samplingbar.cpp | 90 ++--- DSView/pv/toolbars/titlebar.cpp | 189 +++++++++++ DSView/pv/toolbars/titlebar.h | 72 ++++ DSView/pv/view/decodetrace.cpp | 18 +- DSView/pv/view/decodetrace.h | 4 +- DSView/pv/view/devmode.cpp | 3 +- DSView/pv/view/header.cpp | 1 - DSView/pv/view/signal.cpp | 5 + DSView/pv/view/signal.h | 2 + DSView/pv/view/view.cpp | 8 +- DSView/pv/view/view.h | 5 +- DSView/pv/widgets/border.cpp | 107 ++++++ DSView/pv/widgets/border.h | 46 +++ DSView/pv/widgets/decodergroupbox.cpp | 2 +- 64 files changed, 1987 insertions(+), 508 deletions(-) create mode 100755 DSView/icons/close.png create mode 100755 DSView/icons/maximize.png create mode 100755 DSView/icons/minimize.png create mode 100755 DSView/icons/restore.png create mode 100755 DSView/pv/dialogs/dsdialog.cpp create mode 100755 DSView/pv/dialogs/dsdialog.h create mode 100755 DSView/pv/dialogs/dsmessagebox.cpp rename DSView/pv/dialogs/{streamoptions.h => dsmessagebox.h} (52%) mode change 100644 => 100755 create mode 100644 DSView/pv/dialogs/messagebox.cpp create mode 100644 DSView/pv/dialogs/messagebox.h create mode 100755 DSView/pv/dialogs/shadow.cpp create mode 100755 DSView/pv/dialogs/shadow.h delete mode 100644 DSView/pv/dialogs/streamoptions.cpp create mode 100644 DSView/pv/mainframe.cpp create mode 100644 DSView/pv/mainframe.h create mode 100644 DSView/pv/toolbars/titlebar.cpp create mode 100644 DSView/pv/toolbars/titlebar.h create mode 100644 DSView/pv/widgets/border.cpp create mode 100644 DSView/pv/widgets/border.h diff --git a/DSView/DSView.qrc b/DSView/DSView.qrc index 5968cc45..b2799fad 100644 --- a/DSView/DSView.qrc +++ b/DSView/DSView.qrc @@ -68,5 +68,9 @@ icons/Hamming.png icons/Hann.png icons/Rectangle.png + icons/close.png + icons/maximize.png + icons/minimize.png + icons/restore.png diff --git a/DSView/darkstyle/style.qss b/DSView/darkstyle/style.qss index 975afee6..ddf6b5e8 100755 --- a/DSView/darkstyle/style.qss +++ b/DSView/darkstyle/style.qss @@ -531,7 +531,6 @@ QMenu::separator margin-right: 5px; } - QFrame { border-radius: 2px; @@ -800,28 +799,39 @@ QTabBar::tab:bottom:!selected:hover { /* LEFT TABS */ QTabBar::tab:left { color: #b1b1b1; - border: 1px solid #4A4949; + border: 1px transparent #4A4949; border-left: 1px transparent black; - background-color: #302F2F; + background-color: #48576b; padding: 5px; - border-top-right-radius: 2px; - border-bottom-right-radius: 2px; + border-top-right-radius: 0px; + border-bottom-right-radius: 0px; } QTabBar::tab:left:!selected { color: #b1b1b1; - background-color: #201F1F; + background-color: #302F2F; border: 1px transparent #4A4949; border-right: 1px transparent #4A4949; border-top-right-radius: 0px; border-bottom-right-radius: 0px; } -QTabBar::tab:left:!selected:hover { +QTabBar::tab:left:hover { background-color: #48576b; } +QTabBar::tab:left:disabled +{ + color: #3A3939; + background-color: #302F2F; + border: 1px transparent #4A4949; + border-right: 1px transparent #4A4949; + border-top-right-radius: 0px; + border-bottom-right-radius: 0px; +} + + /* RIGHT TABS */ QTabBar::tab:right { @@ -975,6 +985,44 @@ QSlider::handle:vertical { margin: 0 -4px; border-radius: 2px; } +QToolButton#MaximizeButton { + background-color: transparent; + border-left: 1px solid QLinearGradient( x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0.0 #302F2F, stop: 0.3 #606060, + stop: 0.5 #707070, + stop: 0.7 #606060, stop: 1 #302F2F); + border-right: 1px solid QLinearGradient( x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0.0 #302F2F, stop: 0.3 #606060, + stop: 0.5 #707070, + stop: 0.7 #606060, stop: 1 #302F2F); + border-radius: 0px; + margin: 0px; + padding: 0px; +} + +QToolButton#MinimizeButton, +QToolButton#CloseButton { + background-color: transparent; + border: 1px transparent #808080; + border-radius: 0px; + margin: 0px; + padding: 0px; +} + +QToolButton#MinimizeButton:hover, QToolButton#MinimizeButton::menu-button:hover, +QToolButton#MaximizeButton:hover, QToolButton#MaximizeButton::menu-button:hover{ + background-color: QLinearGradient( x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0.0 #302F2F, stop: 0.4 #4E4D4D, + stop: 0.5 #4A4949, + stop: 0.6 #4E4D4D, stop: 1 #302F2F); +} + +QToolButton#CloseButton:hover, QToolButton#CloseButton::menu-button:hover { +background-color: QLinearGradient( x1: 0, y1: 0, x2: 0, y2: 1, + stop: 0.0 #302F2F, stop: 0.2 #A82F2F, + stop: 0.5 #E83E4A, + stop: 0.8 #A82F2F, stop: 1 #302F2F); +} QToolButton { background-color: transparent; diff --git a/DSView/icons/close.png b/DSView/icons/close.png new file mode 100755 index 0000000000000000000000000000000000000000..529f2ed10e43787bbd9fda3fa60703185ac607d3 GIT binary patch literal 173 zcmeAS@N?(olHy`uVBq!ia0vp^93afW1|*O0@9PFqoCO|{#S9GG!XV7ZFl&wkP%z%p z#W6%;YVP^dT!$4zS`u6Bk}loUZpfz^>_Yh+9=*_m=Xb zhKKJpsZV=$|M01(*Imr^IpzOeNZqkJmNDbqjOfEWe@u*b&n|Ls{P#KS(03NGpq8p8 T8^@(Us~9|8{an^LB{Ts5KIA@E literal 0 HcmV?d00001 diff --git a/DSView/icons/maximize.png b/DSView/icons/maximize.png new file mode 100755 index 0000000000000000000000000000000000000000..bb4bffb855e02052ad8655447158c97c2fae48fd GIT binary patch literal 158 zcmeAS@N?(olHy`uVBq!ia0vp^93afW1|*O0@9PFqoCO|{#S9GG!XV7ZFl&wkP%yyL z#W6%;YHj~U-Ub5!=J%@k4L9oLkFeZi-01LSnSyJV|GL7&)0>oyr9UvSHOceaZm8X? zV61=h-D-vlrdPvS%+wfWv4utm-WEH$<7dEq8+*f+b~D%VtZ^fqgRu5%FaQ&7D5hE-&p!$Q@I-7Pb9_gFvm zdFCH-;~qop14a>t0~=N*u~#q!dxm?i`_sSbGpA2Oa_M|8P43&)Ir5XfFo!-*PS$)KzZK|K22WQ%mvv4FO#oIkRU`la literal 0 HcmV?d00001 diff --git a/DSView/main.cpp b/DSView/main.cpp index 58b022b5..08d94c03 100644 --- a/DSView/main.cpp +++ b/DSView/main.cpp @@ -37,7 +37,7 @@ #include "dsapplication.h" #include "pv/devicemanager.h" -#include "pv/mainwindow.h" +#include "pv/mainframe.h" #include "config.h" @@ -134,13 +134,13 @@ int main(int argc, char *argv[]) // Create the device manager, initialise the drivers pv::DeviceManager device_manager(sr_ctx); - // Initialise the main window - pv::MainWindow w(device_manager, open_file); - //QFile qss(":/stylesheet.qss"); - QFile qss(":qdarkstyle/style.qss"); - qss.open(QFile::ReadOnly); - a.setStyleSheet(qss.readAll()); - qss.close(); + // Initialise the main frame + pv::MainFrame w(device_manager, open_file); + //QFile qss(":/stylesheet.qss"); + QFile qss(":qdarkstyle/style.qss"); + qss.open(QFile::ReadOnly); + a.setStyleSheet(qss.readAll()); + qss.close(); w.show(); // Run the application diff --git a/DSView/pv/device/devinst.h b/DSView/pv/device/devinst.h index c5e58d62..dec1b525 100644 --- a/DSView/pv/device/devinst.h +++ b/DSView/pv/device/devinst.h @@ -126,6 +126,7 @@ public: virtual void* get_id() const; signals: + void device_updated(); void config_changed(); protected: diff --git a/DSView/pv/devicemanager.cpp b/DSView/pv/devicemanager.cpp index 45ebbd4c..7b986105 100644 --- a/DSView/pv/devicemanager.cpp +++ b/DSView/pv/devicemanager.cpp @@ -47,8 +47,6 @@ using std::ostringstream; using std::runtime_error; using std::string; -extern char AppDataPath[256]; - namespace pv { DeviceManager::DeviceManager(struct sr_context *sr_ctx) : diff --git a/DSView/pv/dialogs/about.cpp b/DSView/pv/dialogs/about.cpp index b402cb52..93cd4dce 100644 --- a/DSView/pv/dialogs/about.cpp +++ b/DSView/pv/dialogs/about.cpp @@ -21,45 +21,41 @@ */ -#include +#include +#include #include "about.h" -#include - -/* __STDC_FORMAT_MACROS is required for PRIu64 and friends (in C++). */ -#define __STDC_FORMAT_MACROS -#include -#include - namespace pv { namespace dialogs { About::About(QWidget *parent) : - QDialog(parent), - ui(new Ui::About) + DSDialog(parent, true) { - ui->setupUi(this); + QPixmap pix(":/icons/dsl_logo.png"); + _logo = new QLabel(this); + _logo->setPixmap(pix); + _logo->setAlignment(Qt::AlignCenter); - /* Setup the version field */ - ui->versionInfo->setText(tr("%1 %2
%4") - .arg(QApplication::applicationName()) - .arg(QApplication::applicationVersion()) - .arg(QApplication::organizationDomain())); - ui->versionInfo->setOpenExternalLinks(true); + _info = new QLabel(this); + _info->setText(tr("%1 %2
%4") + .arg(QApplication::applicationName()) + .arg(QApplication::applicationVersion()) + .arg(QApplication::organizationDomain())); + _info->setOpenExternalLinks(true); + _info->setAlignment(Qt::AlignCenter); - connect(ui->buttonBox, SIGNAL(accepted()), this, SLOT(accept())); + QVBoxLayout *xlayout = new QVBoxLayout(); + xlayout->addWidget(_logo); + xlayout->addWidget(_info); + + layout()->addLayout(xlayout); + setTitle(tr("About")); + setFixedWidth(500); } About::~About() { - delete ui; -} - -void About::accept() -{ - using namespace Qt; - QDialog::accept(); } } // namespace dialogs diff --git a/DSView/pv/dialogs/about.h b/DSView/pv/dialogs/about.h index d31b7607..59b14233 100644 --- a/DSView/pv/dialogs/about.h +++ b/DSView/pv/dialogs/about.h @@ -24,18 +24,13 @@ #ifndef DSVIEW_PV_ABOUT_H #define DSVIEW_PV_ABOUT_H -#include - -#include - -namespace Ui { -class About; -} +#include +#include "dsdialog.h" namespace pv { namespace dialogs { -class About : public QDialog +class About : public DSDialog { Q_OBJECT @@ -43,11 +38,9 @@ public: explicit About(QWidget *parent = 0); ~About(); -protected: - void accept(); - private: - Ui::About *ui; + QLabel *_logo; + QLabel *_info; }; } // namespace dialogs diff --git a/DSView/pv/dialogs/calibration.cpp b/DSView/pv/dialogs/calibration.cpp index fe2572aa..465affe6 100755 --- a/DSView/pv/dialogs/calibration.cpp +++ b/DSView/pv/dialogs/calibration.cpp @@ -43,11 +43,10 @@ const QString Calibration::VGAIN = tr(" VGAIN"); const QString Calibration::VOFF = tr(" VOFF"); Calibration::Calibration(QWidget *parent) : - QDialog(parent) + DSDialog(parent) { this->setFixedSize(400, 200); this->setWindowOpacity(0.7); - this->setWindowFlags(Qt::Dialog | Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint); this->setModal(false); _dev_inst = NULL; @@ -56,16 +55,19 @@ Calibration::Calibration(QWidget *parent) : _exit_btn = new QPushButton(tr("Exit"), this); _flayout = new QFormLayout(); - QGridLayout *glayout = new QGridLayout(this); - glayout->addLayout(_flayout, 0, 0, 1, 5); - glayout->addWidget(_save_btn, 1, 0); - glayout->addWidget(new QWidget(this), 1, 1); + QGridLayout *glayout = new QGridLayout(); + + glayout->addLayout(_flayout, 1, 0, 1, 5); + glayout->addWidget(_save_btn, 2, 0); + glayout->addWidget(new QWidget(this), 2, 1); glayout->setColumnStretch(1, 1); - glayout->addWidget(_reset_btn, 1, 2); - glayout->addWidget(new QWidget(this), 1, 3); + glayout->addWidget(_reset_btn, 2, 2); + glayout->addWidget(new QWidget(this), 2, 3); glayout->setColumnStretch(3, 1); - glayout->addWidget(_exit_btn, 1, 4); - setLayout(glayout); + glayout->addWidget(_exit_btn, 2, 4); + + layout()->addLayout(glayout); + setTitle(tr("Manual Calibration")); connect(_save_btn, SIGNAL(clicked()), this, SLOT(on_save())); connect(_reset_btn, SIGNAL(clicked()), this, SLOT(on_reset())); diff --git a/DSView/pv/dialogs/calibration.h b/DSView/pv/dialogs/calibration.h index 3cc49827..9e469f1b 100755 --- a/DSView/pv/dialogs/calibration.h +++ b/DSView/pv/dialogs/calibration.h @@ -23,7 +23,6 @@ #ifndef DSVIEW_PV_CALIBRATION_H #define DSVIEW_PV_CALIBRATION_H -#include #include #include #include @@ -34,11 +33,13 @@ #include #include +#include "../toolbars/titlebar.h" +#include "dsdialog.h" namespace pv { namespace dialogs { -class Calibration : public QDialog +class Calibration : public DSDialog { Q_OBJECT @@ -63,6 +64,7 @@ private slots: private: boost::shared_ptr _dev_inst; + toolbars::TitleBar *_titlebar; QPushButton *_save_btn; QPushButton *_reset_btn; QPushButton *_exit_btn; diff --git a/DSView/pv/dialogs/deviceoptions.cpp b/DSView/pv/dialogs/deviceoptions.cpp index b2c8db05..970bf4ad 100644 --- a/DSView/pv/dialogs/deviceoptions.cpp +++ b/DSView/pv/dialogs/deviceoptions.cpp @@ -27,8 +27,8 @@ #include #include -#include +#include "dsmessagebox.h" #include using namespace boost; @@ -38,16 +38,12 @@ namespace pv { namespace dialogs { DeviceOptions::DeviceOptions(QWidget *parent, boost::shared_ptr dev_inst) : - QDialog(parent), + DSDialog(parent), _dev_inst(dev_inst), - _layout(this), _button_box(QDialogButtonBox::Ok, Qt::Horizontal, this), _device_options_binding(_dev_inst->dev_inst()) { - setWindowTitle(tr("Configure Device")); - setLayout(&_layout); - _props_box = new QGroupBox(tr("Mode"), this); _props_box->setLayout(&_props_box_layout); _props_box_layout.addWidget(get_property_form()); @@ -58,7 +54,7 @@ DeviceOptions::DeviceOptions(QWidget *parent, boost::shared_ptrsetLayout(&_probes_box_layout); _layout.addWidget(_probes_box); - } else { + } else if (_dev_inst->name().contains("DSCope")){ _config_button = new QPushButton(tr("Zero Adjustment"), this); _layout.addWidget(_config_button); connect(_config_button, SIGNAL(clicked()), this, SLOT(zero_adj())); @@ -71,8 +67,12 @@ DeviceOptions::DeviceOptions(QWidget *parent, boost::shared_ptraddLayout(&_layout); + setTitle(tr("Device Options")); + connect(&_button_box, SIGNAL(accepted()), this, SLOT(accept())); //connect(&_button_box, SIGNAL(rejected()), this, SLOT(reject())); + connect(_dev_inst.get(), SIGNAL(device_updated()), this, SLOT(reject())); GVariant* gvar = _dev_inst->get_config(NULL, NULL, SR_CONF_OPERATION_MODE); if (gvar != NULL) { @@ -113,7 +113,9 @@ void DeviceOptions::accept() void DeviceOptions::reject() { - accept(); + using namespace Qt; + + QDialog::reject(); } QWidget* DeviceOptions::get_property_form() @@ -232,15 +234,14 @@ void DeviceOptions::zero_adj() using namespace Qt; QDialog::accept(); - QMessageBox msg(this); - msg.setText(tr("Information")); - msg.setInformativeText(tr("Zero adjustment program will be started. Please keep all channels out of singal input. It can take a while!")); - //msg.setStandardButtons(QMessageBox::); - msg.addButton(tr("Ok"), QMessageBox::AcceptRole); - msg.addButton(tr("Cancel"), QMessageBox::RejectRole); - msg.setIcon(QMessageBox::Information); - int ret = msg.exec(); - if ( ret == QMessageBox::AcceptRole) { + dialogs::DSMessageBox msg(this); + msg.mBox()->setText(tr("Information")); + msg.mBox()->setInformativeText(tr("Zero adjustment program will be started. Please keep all channels out of singal input. It can take a while!")); + //msg.mBox()->setStandardButtons(QMessageBox::); + msg.mBox()->addButton(tr("Ok"), QMessageBox::AcceptRole); + msg.mBox()->addButton(tr("Cancel"), QMessageBox::RejectRole); + msg.mBox()->setIcon(QMessageBox::Information); + if (msg.exec()) { _dev_inst->set_config(NULL, NULL, SR_CONF_ZERO, g_variant_new_boolean(true)); } } diff --git a/DSView/pv/dialogs/deviceoptions.h b/DSView/pv/dialogs/deviceoptions.h index e1c3c6a3..c2dd9ee2 100644 --- a/DSView/pv/dialogs/deviceoptions.h +++ b/DSView/pv/dialogs/deviceoptions.h @@ -42,11 +42,13 @@ #include #include +#include "../toolbars/titlebar.h" +#include "../dialogs/dsdialog.h" namespace pv { namespace dialogs { -class DeviceOptions : public QDialog +class DeviceOptions : public DSDialog { Q_OBJECT @@ -76,6 +78,7 @@ private slots: private: boost::shared_ptr _dev_inst; QVBoxLayout _layout; + toolbars::TitleBar *_titlebar; QGroupBox *_probes_box; QGridLayout _probes_box_layout; diff --git a/DSView/pv/dialogs/dsdialog.cpp b/DSView/pv/dialogs/dsdialog.cpp new file mode 100755 index 00000000..a8f0202e --- /dev/null +++ b/DSView/pv/dialogs/dsdialog.cpp @@ -0,0 +1,144 @@ +/* + * This file is part of the DSView project. + * DSView is based on PulseView. + * + * Copyright (C) 2016 DreamSourceLab + * + * 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 "dsdialog.h" +#include "shadow.h" + +#include +#include +#include +#include +#include + +namespace pv { +namespace dialogs { + +DSDialog::DSDialog(QWidget *parent, bool hasClose) : + QDialog(parent), + _moving(false) +{ + setWindowFlags(Qt::FramelessWindowHint | Qt::Dialog); + setAttribute(Qt::WA_TranslucentBackground); + + build_main(hasClose); + + _layout = new QVBoxLayout(this); + _layout->addWidget(_main); + setLayout(_layout); +} + +void DSDialog::accept() +{ + using namespace Qt; + + QDialog::accept(); +} + +void DSDialog::reject() +{ + using namespace Qt; + + QDialog::reject(); +} + +bool DSDialog::eventFilter(QObject *object, QEvent *event) +{ + const QEvent::Type type = event->type(); + const QMouseEvent *const mouse_event = (QMouseEvent*)event; + if (type == QEvent::MouseMove) { + if (_moving && mouse_event->buttons().testFlag(Qt::LeftButton)) { + move(mouse_event->globalPos() - _startPos); + } + return true; + } else if (type == QEvent::MouseButtonPress) { + if (mouse_event->buttons().testFlag(Qt::LeftButton)) { + _moving = true; +#ifndef _WIN32 + _startPos = mouse_event->pos() + + QPoint(_layout->margin(), _layout->margin()) + + QPoint(_layout->spacing(), _layout->spacing()) + + QPoint(_mlayout->margin(), _mlayout->margin()) + + QPoint(_mlayout->spacing(), _mlayout->spacing()); +#else + _startPos = mouse_event->pos() + + QPoint(_layout->margin(), _layout->margin()) + + QPoint(_layout->spacing(), _layout->spacing()); +#endif + } + } else if (type == QEvent::MouseButtonRelease) { + if (mouse_event->buttons().testFlag(Qt::LeftButton)) { + _moving = false; + } + } + return false; +} + +QVBoxLayout* DSDialog::layout() +{ + return _mlayout; +} + +QWidget* DSDialog::mainWidget() +{ + return _main; +} + +void DSDialog::setTitle(QString title) +{ + _titlebar->setTitle(title); +} + +void DSDialog::reload(bool hasClose) +{ + QString title; + if (_titlebar) + title = _titlebar->title(); + if (_main) + delete _main; + + build_main(hasClose); + _titlebar->setTitle(title); + _layout->addWidget(_main); +} + +void DSDialog::build_main(bool hasClose) +{ + _main = new QWidget(this); + _mlayout = new QVBoxLayout(_main); + _main->setLayout(_mlayout); + //_mlayout->setMargin(5); + //_mlayout->setSpacing(5); + + Shadow *bodyShadow = new Shadow(_main); + bodyShadow->setBlurRadius(10.0); + bodyShadow->setDistance(3.0); + bodyShadow->setColor(QColor(0, 0, 0, 80)); + _main->setAutoFillBackground(true); + _main->setGraphicsEffect(bodyShadow); + + _titlebar = new toolbars::TitleBar(false, this, hasClose); + _titlebar->installEventFilter(this); + _mlayout->addWidget(_titlebar); +} + +} // namespace dialogs +} // namespace pv diff --git a/DSView/pv/dialogs/dsdialog.h b/DSView/pv/dialogs/dsdialog.h new file mode 100755 index 00000000..cefc614d --- /dev/null +++ b/DSView/pv/dialogs/dsdialog.h @@ -0,0 +1,68 @@ +/* + * This file is part of the DSView project. + * DSView is based on PulseView. + * + * Copyright (C) 2016 DreamSourceLab + * + * 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 + */ + + +#ifndef DSVIEW_PV_DSDIALOG_H +#define DSVIEW_PV_DSDIALOG_H + +#include +#include +#include + +#include "../toolbars/titlebar.h" + +namespace pv { +namespace dialogs { + +class DSDialog : public QDialog +{ + Q_OBJECT + +public: + DSDialog(QWidget *parent = 0, bool hasClose = false); + QVBoxLayout *layout(); + QWidget *mainWidget(); + + void setTitle(QString title); + void reload(bool hasClose); + +protected: + void accept(); + void reject(); + //void mousePressEvent(QMouseEvent *event); + //void mouseReleaseEvent(QMouseEvent *event); + bool eventFilter(QObject *object, QEvent *event); +private: + void build_main(bool hasClose); + +private: + QVBoxLayout *_layout; + QVBoxLayout *_mlayout; + QWidget *_main; + toolbars::TitleBar *_titlebar; + bool _moving; + QPoint _startPos; +}; + +} // namespace dialogs +} // namespace pv + +#endif // DSVIEW_PV_DSDIALOG_H diff --git a/DSView/pv/dialogs/dsmessagebox.cpp b/DSView/pv/dialogs/dsmessagebox.cpp new file mode 100755 index 00000000..d7e44441 --- /dev/null +++ b/DSView/pv/dialogs/dsmessagebox.cpp @@ -0,0 +1,129 @@ +/* + * This file is part of the DSView project. + * DSView is based on PulseView. + * + * Copyright (C) 2016 DreamSourceLab + * + * 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 "dsmessagebox.h" +#include "shadow.h" + +#include +#include +#include +#include +#include + +namespace pv { +namespace dialogs { + +DSMessageBox::DSMessageBox(QWidget *parent) : + QDialog(parent), + _moving(false) +{ + setWindowFlags(Qt::FramelessWindowHint | Qt::Dialog); + setAttribute(Qt::WA_TranslucentBackground); + _main = new QWidget(this); + QVBoxLayout *mlayout = new QVBoxLayout(_main); + _main->setLayout(mlayout); + + Shadow *bodyShadow = new Shadow(); + bodyShadow->setBlurRadius(10.0); + bodyShadow->setDistance(3.0); + bodyShadow->setColor(QColor(0, 0, 0, 80)); + _main->setAutoFillBackground(true); + _main->setGraphicsEffect(bodyShadow); + + _msg = new QMessageBox(this); + _msg->setWindowFlags(Qt::FramelessWindowHint | Qt::Widget); + + _titlebar = new toolbars::TitleBar(false, this); + _titlebar->setTitle(tr("Message")); + _titlebar->installEventFilter(this); + + mlayout->addWidget(_titlebar); + mlayout->addWidget(_msg); + + _layout = new QVBoxLayout(this); + _layout->addWidget(_main); + setLayout(_layout); + + //connect(_msg, SIGNAL(finished(int)), this, SLOT(accept())); + connect(_msg, SIGNAL(buttonClicked(QAbstractButton*)), this, SLOT(on_button(QAbstractButton*))); +} + +void DSMessageBox::accept() +{ + using namespace Qt; + + QDialog::accept(); +} + +void DSMessageBox::reject() +{ + using namespace Qt; + + QDialog::reject(); +} + +bool DSMessageBox::eventFilter(QObject *object, QEvent *event) +{ + const QEvent::Type type = event->type(); + const QMouseEvent *const mouse_event = (QMouseEvent*)event; + if (type == QEvent::MouseMove) { + if (_moving && mouse_event->buttons().testFlag(Qt::LeftButton)) { + move(mouse_event->globalPos() - _startPos); + } + return true; + } else if (type == QEvent::MouseButtonPress) { + if (mouse_event->buttons().testFlag(Qt::LeftButton)) { + _moving = true; + _startPos = mouse_event->pos() + + QPoint(_layout->margin(), _layout->margin()) + + QPoint(_layout->spacing(), _layout->spacing()); + } + } else if (type == QEvent::MouseButtonRelease) { + if (mouse_event->buttons().testFlag(Qt::LeftButton)) { + _moving = false; + } + } + return false; +} + +QMessageBox* DSMessageBox::mBox() +{ + return _msg; +} + +int DSMessageBox::exec() +{ + //_msg->show(); + return QDialog::exec(); +} + +void DSMessageBox::on_button(QAbstractButton *btn) +{ + QMessageBox::ButtonRole role = _msg->buttonRole(btn); + if (role == QMessageBox::AcceptRole) + accept(); + else + reject(); +} + +} // namespace dialogs +} // namespace pv diff --git a/DSView/pv/dialogs/streamoptions.h b/DSView/pv/dialogs/dsmessagebox.h old mode 100644 new mode 100755 similarity index 52% rename from DSView/pv/dialogs/streamoptions.h rename to DSView/pv/dialogs/dsmessagebox.h index 1c9c148d..5fc2ea30 --- a/DSView/pv/dialogs/streamoptions.h +++ b/DSView/pv/dialogs/dsmessagebox.h @@ -2,7 +2,7 @@ * This file is part of the DSView project. * DSView is based on PulseView. * - * Copyright (C) 2013 DreamSourceLab + * Copyright (C) 2016 DreamSourceLab * * 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 @@ -20,56 +20,49 @@ */ -#ifndef DSVIEW_PV_STREAMOPTIONS_H -#define DSVIEW_PV_STREAMOPTIONS_H +#ifndef DSVIEW_PV_DSMESSAGEBOX_H +#define DSVIEW_PV_DSMESSAGEBOX_H #include -#include -#include +#include +#include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include +#include "../toolbars/titlebar.h" namespace pv { namespace dialogs { -class StreamOptions : public QDialog +class DSMessageBox : public QDialog { Q_OBJECT public: - StreamOptions(QWidget *parent, boost::shared_ptr dev_inst, - uint64_t sample_count, bool stream); + DSMessageBox(QWidget *parent); + QMessageBox *mBox(); + + int exec(); protected: - void accept(); + void accept(); void reject(); + //void mousePressEvent(QMouseEvent *event); + //void mouseReleaseEvent(QMouseEvent *event); + bool eventFilter(QObject *object, QEvent *event); + +private slots: + void on_button(QAbstractButton* btn); private: - boost::shared_ptr _dev_inst; - uint64_t _sample_count; - QVBoxLayout _layout; - - QRadioButton * _op0; - QRadioButton * _op1; - - bool _stream; - - QDialogButtonBox _button_box; + QVBoxLayout *_layout; + QWidget *_main; + QMessageBox *_msg; + toolbars::TitleBar *_titlebar; + bool _moving; + QPoint _startPos; }; } // namespace dialogs } // namespace pv -#endif // DSVIEW_PV_STREAMOPTIONS_H +#endif // DSVIEW_PV_DSMESSAGEBOX_H diff --git a/DSView/pv/dialogs/dsomeasure.cpp b/DSView/pv/dialogs/dsomeasure.cpp index 6f5921fd..f59e7c9a 100644 --- a/DSView/pv/dialogs/dsomeasure.cpp +++ b/DSView/pv/dialogs/dsomeasure.cpp @@ -21,6 +21,7 @@ #include "dsomeasure.h" +#include "../device/devinst.h" #include #include @@ -35,14 +36,12 @@ namespace pv { namespace dialogs { DsoMeasure::DsoMeasure(QWidget *parent, boost::shared_ptr dsoSig) : - QDialog(parent), + DSDialog(parent), _dsoSig(dsoSig), - _layout(this), _button_box(QDialogButtonBox::Ok, Qt::Horizontal, this) { - setWindowTitle(tr("DSO Measure Options")); - setLayout(&_layout); + setMinimumWidth(300); for (int i=DsoSignal::DSO_MS_BEGIN+1; iget_ms_string(i), this); @@ -54,8 +53,13 @@ DsoMeasure::DsoMeasure(QWidget *parent, boost::shared_ptr dsoSig) : _layout.addWidget(&_button_box); + layout()->addLayout(&_layout); + setTitle(tr("Measurements")); + connect(&_button_box, SIGNAL(accepted()), this, SLOT(accept())); connect(&_button_box, SIGNAL(rejected()), this, SLOT(accept())); + + connect(_dsoSig->get_device().get(), SIGNAL(device_updated()), this, SLOT(reject())); } void DsoMeasure::set_measure(bool en) @@ -77,7 +81,9 @@ void DsoMeasure::accept() void DsoMeasure::reject() { - accept(); + using namespace Qt; + + QDialog::reject(); } } // namespace dialogs diff --git a/DSView/pv/dialogs/dsomeasure.h b/DSView/pv/dialogs/dsomeasure.h index 178b0732..a50689be 100644 --- a/DSView/pv/dialogs/dsomeasure.h +++ b/DSView/pv/dialogs/dsomeasure.h @@ -23,13 +23,14 @@ #ifndef DSVIEW_PV_DSOMEASURE_H #define DSVIEW_PV_DSOMEASURE_H -#include #include #include #include -#include +#include "../view/dsosignal.h" +#include "../toolbars/titlebar.h" +#include "dsdialog.h" namespace pv { @@ -39,7 +40,7 @@ class DsoSignal; namespace dialogs { -class DsoMeasure : public QDialog +class DsoMeasure : public DSDialog { Q_OBJECT @@ -55,7 +56,7 @@ protected: private: boost::shared_ptr _dsoSig; - + toolbars::TitleBar *_titlebar; QVBoxLayout _layout; QDialogButtonBox _button_box; }; diff --git a/DSView/pv/dialogs/fftoptions.cpp b/DSView/pv/dialogs/fftoptions.cpp index d2344ca3..ebe8b0b1 100644 --- a/DSView/pv/dialogs/fftoptions.cpp +++ b/DSView/pv/dialogs/fftoptions.cpp @@ -26,7 +26,6 @@ #include #include -#include #include "../sigsession.h" #include "../data/mathstack.h" @@ -41,7 +40,7 @@ namespace pv { namespace dialogs { FftOptions::FftOptions(QWidget *parent, SigSession &session) : - QDialog(parent), + DSDialog(parent), _session(session), _button_box(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, this) @@ -188,15 +187,18 @@ FftOptions::FftOptions(QWidget *parent, SigSession &session) : _hint_label->setPixmap(pixmap); _hlayout->addWidget(_hint_label); - _layout = new QVBoxLayout(this); + _layout = new QVBoxLayout(); _layout->addLayout(_hlayout); _layout->addWidget(&_button_box); - setLayout(_layout); + + layout()->addLayout(_layout); + setTitle(tr("FFT Options")); connect(&_button_box, SIGNAL(accepted()), this, SLOT(accept())); connect(&_button_box, SIGNAL(rejected()), this, SLOT(reject())); connect(_window_combobox, SIGNAL(currentIndexChanged(QString)), this, SLOT(window_changed(QString))); connect(_len_combobox, SIGNAL(currentIndexChanged(int)), this, SLOT(len_changed(int))); + connect(_session.get_device().get(), SIGNAL(device_updated()), this, SLOT(reject())); } void FftOptions::accept() diff --git a/DSView/pv/dialogs/fftoptions.h b/DSView/pv/dialogs/fftoptions.h index 2ef9a8e9..9dcb20e4 100644 --- a/DSView/pv/dialogs/fftoptions.h +++ b/DSView/pv/dialogs/fftoptions.h @@ -23,7 +23,6 @@ #ifndef DSVIEW_PV_FFTOPTIONS_H #define DSVIEW_PV_FFTOPTIONS_H -#include #include #include #include @@ -34,6 +33,8 @@ #include #include "../device/devinst.h" +#include "../toolbars/titlebar.h" +#include "dsdialog.h" namespace pv { @@ -41,7 +42,7 @@ class SigSession; namespace dialogs { -class FftOptions : public QDialog +class FftOptions : public DSDialog { Q_OBJECT @@ -63,6 +64,7 @@ private: SigSession &_session; uint64_t _sample_limit; + toolbars::TitleBar *_titlebar; QComboBox *_len_combobox; QComboBox *_interval_combobox; QCheckBox *_en_checkbox; diff --git a/DSView/pv/dialogs/messagebox.cpp b/DSView/pv/dialogs/messagebox.cpp new file mode 100644 index 00000000..d0ab4eda --- /dev/null +++ b/DSView/pv/dialogs/messagebox.cpp @@ -0,0 +1,86 @@ +/* + * This file is part of the DSView project. + * DSView is based on PulseView. + * + * Copyright (C) 2016 DreamSourceLab + * + * 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 "messagebox.h" + +#include +#include + +namespace pv { +namespace dialogs { + +MessageBox::MessageBox(QWidget *parent) : + QDialog(parent) +{ + setWindowFlags(Qt::FramelessWindowHint | Qt::Dialog); + + _msg = new QMessageBox(this); + _msg->setWindowFlags(Qt::FramelessWindowHint | Qt::Widget); + + _titlebar = new toolbars::TitleBar(false, parent); + _titlebar->setTitle(tr("Message")); + + QVBoxLayout *layout = new QVBoxLayout(this); + layout->addWidget(_titlebar); + layout->addWidget(_msg); + setLayout(layout); + + //connect(_msg, SIGNAL(finished(int)), this, SLOT(accept())); + connect(_msg, SIGNAL(buttonClicked(QAbstractButton*)), this, SLOT(on_button(QAbstractButton*))); +} + +void MessageBox::accept() +{ + using namespace Qt; + + QDialog::accept(); +} + +void MessageBox::reject() +{ + using namespace Qt; + + QDialog::reject(); +} + +QMessageBox* MessageBox::mBox() +{ + return _msg; +} + +int MessageBox::exec() +{ + //_msg->show(); + return QDialog::exec(); +} + +void MessageBox::on_button(QAbstractButton *btn) +{ + QMessageBox::ButtonRole role = _msg->buttonRole(btn); + if (role == QMessageBox::AcceptRole) + accept(); + else + reject(); +} + +} // namespace dialogs +} // namespace pv diff --git a/DSView/pv/dialogs/messagebox.h b/DSView/pv/dialogs/messagebox.h new file mode 100644 index 00000000..ca9170b3 --- /dev/null +++ b/DSView/pv/dialogs/messagebox.h @@ -0,0 +1,59 @@ +/* + * This file is part of the DSView project. + * DSView is based on PulseView. + * + * Copyright (C) 2016 DreamSourceLab + * + * 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 + */ + + +#ifndef DSVIEW_PV_MESSAGE_H +#define DSVIEW_PV_MESSAGE_H + +#include +#include + +#include "../toolbars/titlebar.h" + +namespace pv { +namespace dialogs { + +class MessageBox : public QDialog +{ + Q_OBJECT + +public: + MessageBox(QWidget *parent); + QMessageBox *mBox(); + + int exec(); + +protected: + void accept(); + void reject(); + +private slots: + void on_button(QAbstractButton* btn); + +private: + QMessageBox *_msg; + toolbars::TitleBar *_titlebar; +}; + +} // namespace dialogs +} // namespace pv + +#endif // DSVIEW_PV_MESSAGE_H diff --git a/DSView/pv/dialogs/protocolexp.cpp b/DSView/pv/dialogs/protocolexp.cpp index af0ef69c..b4a6f988 100644 --- a/DSView/pv/dialogs/protocolexp.cpp +++ b/DSView/pv/dialogs/protocolexp.cpp @@ -26,7 +26,6 @@ #include #include -#include #include #include #include @@ -48,7 +47,7 @@ namespace pv { namespace dialogs { ProtocolExp::ProtocolExp(QWidget *parent, SigSession &session) : - QDialog(parent), + DSDialog(parent), _session(session), _button_box(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, this), @@ -84,13 +83,17 @@ ProtocolExp::ProtocolExp(QWidget *parent, SigSession &session) : } } - _layout = new QVBoxLayout(this); + _layout = new QVBoxLayout(); _layout->addLayout(_flayout); _layout->addWidget(&_button_box); - setLayout(_layout); + + layout()->addLayout(_layout); + setTitle(tr("Protocol Export")); connect(&_button_box, SIGNAL(accepted()), this, SLOT(accept())); connect(&_button_box, SIGNAL(rejected()), this, SLOT(reject())); + connect(_session.get_device().get(), SIGNAL(device_updated()), this, SLOT(reject())); + } void ProtocolExp::accept() diff --git a/DSView/pv/dialogs/protocolexp.h b/DSView/pv/dialogs/protocolexp.h index 0323082d..b978c604 100644 --- a/DSView/pv/dialogs/protocolexp.h +++ b/DSView/pv/dialogs/protocolexp.h @@ -23,7 +23,6 @@ #ifndef DSVIEW_PV_PROTOCOLEXP_H #define DSVIEW_PV_PROTOCOLEXP_H -#include #include #include #include @@ -35,6 +34,8 @@ #include "../device/devinst.h" #include "../prop/binding/deviceoptions.h" +#include "../toolbars/titlebar.h" +#include "dsdialog.h" namespace pv { @@ -48,7 +49,7 @@ class Row; namespace dialogs { -class ProtocolExp : public QDialog +class ProtocolExp : public DSDialog { Q_OBJECT @@ -68,6 +69,7 @@ private slots: private: SigSession &_session; + toolbars::TitleBar *_titlebar; QComboBox *_format_combobox; std::list _row_sel_list; std::list _row_label_list; diff --git a/DSView/pv/dialogs/protocollist.cpp b/DSView/pv/dialogs/protocollist.cpp index da2451a0..e8d4b5a8 100644 --- a/DSView/pv/dialogs/protocollist.cpp +++ b/DSView/pv/dialogs/protocollist.cpp @@ -26,7 +26,6 @@ #include #include -#include #include "../sigsession.h" #include "../data/decoderstack.h" @@ -41,7 +40,7 @@ namespace pv { namespace dialogs { ProtocolList::ProtocolList(QWidget *parent, SigSession &session) : - QDialog(parent), + DSDialog(parent), _session(session), _button_box(QDialogButtonBox::Ok, Qt::Horizontal, this) @@ -66,14 +65,19 @@ ProtocolList::ProtocolList(QWidget *parent, SigSession &session) : _flayout = new QFormLayout(); _flayout->addRow(new QLabel(tr("Decoded Protocols: "), this), _protocol_combobox); - _layout = new QVBoxLayout(this); + _layout = new QVBoxLayout(); _layout->addLayout(_flayout); _layout->addWidget(&_button_box); - setLayout(_layout); + + setMinimumWidth(300); + layout()->addLayout(_layout); + setTitle(tr("Protocol List Viewer")); connect(&_button_box, SIGNAL(accepted()), this, SLOT(accept())); connect(_protocol_combobox, SIGNAL(currentIndexChanged(int)), this, SLOT(set_protocol(int))); set_protocol(_protocol_combobox->currentIndex()); + connect(_session.get_device().get(), SIGNAL(device_updated()), this, SLOT(reject())); + } void ProtocolList::accept() diff --git a/DSView/pv/dialogs/protocollist.h b/DSView/pv/dialogs/protocollist.h index 0f116f7a..8c4524fd 100644 --- a/DSView/pv/dialogs/protocollist.h +++ b/DSView/pv/dialogs/protocollist.h @@ -23,7 +23,6 @@ #ifndef DSVIEW_PV_PROTOCOLLIST_H #define DSVIEW_PV_PROTOCOLLIST_H -#include #include #include #include @@ -35,6 +34,8 @@ #include "../device/devinst.h" #include "../prop/binding/deviceoptions.h" +#include "../toolbars/titlebar.h" +#include "dsdialog.h" namespace pv { @@ -42,7 +43,7 @@ class SigSession; namespace dialogs { -class ProtocolList : public QDialog +class ProtocolList : public DSDialog { Q_OBJECT @@ -60,6 +61,7 @@ private slots: private: SigSession &_session; + toolbars::TitleBar *_titlebar; QComboBox *_protocol_combobox; std::list _show_checkbox_list; std::list _show_label_list; diff --git a/DSView/pv/dialogs/search.cpp b/DSView/pv/dialogs/search.cpp index 3f177f60..5f41fc44 100644 --- a/DSView/pv/dialogs/search.cpp +++ b/DSView/pv/dialogs/search.cpp @@ -27,11 +27,11 @@ namespace pv { namespace dialogs { -Search::Search(QWidget *parent, struct sr_dev_inst *sdi, QString pattern) : - QDialog(parent), - _sdi(sdi) +Search::Search(QWidget *parent, boost::shared_ptr dev_inst, QString pattern) : + DSDialog(parent), + _dev_inst(dev_inst) { - assert(_sdi); + assert(_dev_inst); QFont font("Monaco"); font.setStyleHint(QFont::Monospace); @@ -54,18 +54,19 @@ Search::Search(QWidget *parent, struct sr_dev_inst *sdi, QString pattern) : search_buttonBox.addButton(QDialogButtonBox::Cancel); QGridLayout *search_layout = new QGridLayout(); - search_layout->addWidget(search_label, 0, 1); - search_layout->addWidget(new QLabel(tr("Search Value: ")), 1,0, Qt::AlignRight); - search_layout->addWidget(&search_lineEdit, 1, 1); - search_layout->addWidget(new QLabel(" "), 2,0); - search_layout->addWidget(new QLabel(tr("X: Don't care\n0: Low level\n1: High level\nR: Rising edge\nF: Falling edge\nC: Rising/Falling edge")), 3, 0); - search_layout->addWidget(&search_buttonBox, 4, 2); - //search_layout->addStretch(1); + search_layout->addWidget(search_label, 1, 1); + search_layout->addWidget(new QLabel(tr("Search Value: ")), 2,0, Qt::AlignRight); + search_layout->addWidget(&search_lineEdit, 2, 1); + search_layout->addWidget(new QLabel(" "), 3,0); + search_layout->addWidget(new QLabel(tr("X: Don't care\n0: Low level\n1: High level\nR: Rising edge\nF: Falling edge\nC: Rising/Falling edge")), 4, 0); + search_layout->addWidget(&search_buttonBox, 5, 2); - setLayout(search_layout); + layout()->addLayout(search_layout); + setTitle(tr("Search Options")); connect(&search_buttonBox, SIGNAL(accepted()), this, SLOT(accept())); connect(&search_buttonBox, SIGNAL(rejected()), this, SLOT(reject())); + connect(_dev_inst.get(), SIGNAL(device_updated()), this, SLOT(reject())); } Search::~Search() diff --git a/DSView/pv/dialogs/search.h b/DSView/pv/dialogs/search.h index 9efd762d..7a9d15ac 100644 --- a/DSView/pv/dialogs/search.h +++ b/DSView/pv/dialogs/search.h @@ -23,7 +23,6 @@ #ifndef DSVIEW_PV_SEARCH_H #define DSVIEW_PV_SEARCH_H -#include #include #include #include @@ -31,17 +30,22 @@ #include #include "../sigsession.h" #include +#include "../toolbars/titlebar.h" +#include "dsdialog.h" +#include "../device/devinst.h" + +#include namespace pv { namespace dialogs { -class Search : public QDialog +class Search : public DSDialog { Q_OBJECT public: - Search(QWidget *parent = 0, sr_dev_inst *sdi = 0, QString pattern = ""); + Search(QWidget *parent = 0, boost::shared_ptr dev_inst = 0, QString pattern = ""); ~Search(); QString get_pattern(); @@ -54,9 +58,10 @@ signals: public slots: private: + toolbars::TitleBar *_titlebar; QLineEdit search_lineEdit; QDialogButtonBox search_buttonBox; - sr_dev_inst *_sdi; + boost::shared_ptr _dev_inst; }; } // namespace decoder diff --git a/DSView/pv/dialogs/shadow.cpp b/DSView/pv/dialogs/shadow.cpp new file mode 100755 index 00000000..0fc91ee4 --- /dev/null +++ b/DSView/pv/dialogs/shadow.cpp @@ -0,0 +1,104 @@ +/* + * This file is part of the DSView project. + * DSView is based on PulseView. + * + * Copyright (C) 2016 DreamSourceLab + * + * 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 "shadow.h" +#include + +QT_BEGIN_NAMESPACE + extern Q_WIDGETS_EXPORT void qt_blurImage(QPainter *p, QImage &blurImage, qreal radius, bool quality, bool alphaOnly, int transposed = 0 ); +QT_END_NAMESPACE + +namespace pv { +namespace dialogs { + +Shadow::Shadow(QObject *parent) : + QGraphicsEffect(parent), + _distance(4.0f), + _blurRadius(10.0f), + _color(0, 0, 0, 80) +{ +} + +void Shadow::draw(QPainter* painter) +{ + // if nothing to show outside the item, just draw source + if ((blurRadius() + distance()) <= 0) { + drawSource(painter); + return; + } + + PixmapPadMode mode = QGraphicsEffect::PadToEffectiveBoundingRect; + QPoint offset; + const QPixmap px = sourcePixmap(Qt::DeviceCoordinates, &offset, mode); + + // return if no source + if (px.isNull()) + return; + + // save world transform + QTransform restoreTransform = painter->worldTransform(); + painter->setWorldTransform(QTransform()); + + // Calculate size for the background image + QSize szi(px.size().width() + 2 * distance(), px.size().height() + 2 * distance()); + + QImage tmp(szi, QImage::Format_ARGB32_Premultiplied); + QPixmap scaled = px.scaled(szi); + tmp.fill(0); + QPainter tmpPainter(&tmp); + tmpPainter.setCompositionMode(QPainter::CompositionMode_Source); + tmpPainter.drawPixmap(QPointF(-distance(), -distance()), scaled); + tmpPainter.end(); + + // blur the alpha channel + QImage blurred(tmp.size(), QImage::Format_ARGB32_Premultiplied); + blurred.fill(0); + QPainter blurPainter(&blurred); + qt_blurImage(&blurPainter, tmp, blurRadius(), false, true); + blurPainter.end(); + + tmp = blurred; + + // blacken the image... + tmpPainter.begin(&tmp); + tmpPainter.setCompositionMode(QPainter::CompositionMode_SourceIn); + tmpPainter.fillRect(tmp.rect(), color()); + tmpPainter.end(); + + // draw the blurred shadow... + painter->drawImage(offset, tmp); + + // draw the actual pixmap... + painter->drawPixmap(offset, px, QRectF()); + + // restore world transform + painter->setWorldTransform(restoreTransform); +} + +QRectF Shadow::boundingRectFor(const QRectF& rect) const +{ + qreal delta = blurRadius() + distance(); + return rect.united(rect.adjusted(-delta, -delta, delta, delta)); +} + +} // namespace dialogs +} // namespace pv diff --git a/DSView/pv/dialogs/shadow.h b/DSView/pv/dialogs/shadow.h new file mode 100755 index 00000000..e37bbbad --- /dev/null +++ b/DSView/pv/dialogs/shadow.h @@ -0,0 +1,59 @@ +/* + * This file is part of the DSView project. + * DSView is based on PulseView. + * + * Copyright (C) 2016 DreamSourceLab + * + * 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 + */ + + +#ifndef DSVIEW_PV_SHADOW_H +#define DSVIEW_PV_SHADOW_H + +#include +#include + +namespace pv { +namespace dialogs { + +class Shadow : public QGraphicsEffect +{ + Q_OBJECT +public: + explicit Shadow(QObject *parent = 0); + + void draw(QPainter* painter); + QRectF boundingRectFor(const QRectF& rect) const; + + inline void setDistance(qreal distance) { _distance = distance; updateBoundingRect(); } + inline qreal distance() const { return _distance; } + + inline void setBlurRadius(qreal blurRadius) { _blurRadius = blurRadius; updateBoundingRect(); } + inline qreal blurRadius() const { return _blurRadius; } + + inline void setColor(const QColor& color) { _color = color; } + inline QColor color() const { return _color; } + +private: + qreal _distance; + qreal _blurRadius; + QColor _color; +}; + +} // namespace dialogs +} // namespace pv + +#endif // DSVIEW_PV_SHADOW_H diff --git a/DSView/pv/dialogs/storeprogress.cpp b/DSView/pv/dialogs/storeprogress.cpp index f608f548..cbd57200 100644 --- a/DSView/pv/dialogs/storeprogress.cpp +++ b/DSView/pv/dialogs/storeprogress.cpp @@ -19,8 +19,7 @@ */ #include "storeprogress.h" - -#include +#include "dsmessagebox.h" namespace pv { namespace dialogs { @@ -49,11 +48,11 @@ void StoreProgress::run() void StoreProgress::show_error() { - QMessageBox msg(parentWidget()); - msg.setText(tr("Failed to save session.")); - msg.setInformativeText(_session.error()); - msg.setStandardButtons(QMessageBox::Ok); - msg.setIcon(QMessageBox::Warning); + dialogs::DSMessageBox msg(parentWidget()); + msg.mBox()->setText(tr("Failed to save session.")); + msg.mBox()->setInformativeText(_session.error()); + msg.mBox()->setStandardButtons(QMessageBox::Ok); + msg.mBox()->setIcon(QMessageBox::Warning); msg.exec(); } diff --git a/DSView/pv/dialogs/storeprogress.h b/DSView/pv/dialogs/storeprogress.h index 25c77ab0..c582e3e1 100644 --- a/DSView/pv/dialogs/storeprogress.h +++ b/DSView/pv/dialogs/storeprogress.h @@ -28,6 +28,7 @@ #include #include +#include "../toolbars/titlebar.h" namespace pv { @@ -57,6 +58,8 @@ private slots: private: pv::StoreSession _session; + + toolbars::TitleBar *_titlebar; }; } // dialogs diff --git a/DSView/pv/dialogs/streamoptions.cpp b/DSView/pv/dialogs/streamoptions.cpp deleted file mode 100644 index 7c0432dd..00000000 --- a/DSView/pv/dialogs/streamoptions.cpp +++ /dev/null @@ -1,106 +0,0 @@ -/* - * This file is part of the DSView project. - * DSView is based on PulseView. - * - * Copyright (C) 2013 DreamSourceLab - * - * 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 "streamoptions.h" - -#include - -#include -#include -#include - -#include - -using namespace boost; -using namespace std; - -namespace pv { -namespace dialogs { - -StreamOptions::StreamOptions(QWidget *parent, boost::shared_ptr dev_inst, - uint64_t sample_count, bool stream) : - QDialog(parent), - _dev_inst(dev_inst), - _sample_count(sample_count), - _layout(this), - _stream(stream), - _button_box(QDialogButtonBox::Ok, - Qt::Horizontal, this) -{ - setWindowTitle(tr("Stream Mode Options")); - setLayout(&_layout); - - QLabel *_info = new QLabel(this); - if (_stream) - _info->setText(tr("Stream Mode Active!")); - else - _info->setText(tr("Buffer Mode Active!")); - - _layout.addWidget(_info); - - if (_stream) { - _op0 = new QRadioButton(this); - _op1 = new QRadioButton(this); - _op0->setText(tr("16 Channels, Max 10MHz sample rate")); - _op1->setText(tr(" 8 Channels, Max 25MHz sample rate")); - _layout.addWidget(_op0); - _layout.addWidget(_op1); - - if (_sample_count >= SR_GB(1)) { - _op0->setDisabled(true); - _op1->setChecked(true); - }else{ - _op0->setChecked(true); - } - } - - _layout.addWidget(&_button_box); - - connect(&_button_box, SIGNAL(accepted()), this, SLOT(accept())); - connect(&_button_box, SIGNAL(rejected()), this, SLOT(accept())); -} - -void StreamOptions::accept() -{ - using namespace Qt; - - uint64_t sample_rate = _dev_inst->get_sample_rate(); - if (_stream) { - if (_op0->isChecked()) - sample_rate = (sample_rate <= SR_MHZ(10)) ? sample_rate : SR_MHZ(10); - else if (_op1->isChecked()) - sample_rate = (sample_rate > SR_MHZ(10) && sample_rate <= SR_MHZ(25)) ? sample_rate : SR_MHZ(25); - } - _dev_inst->set_config(NULL, NULL, - SR_CONF_SAMPLERATE, - g_variant_new_uint64(sample_rate)); - - QDialog::accept(); -} - -void StreamOptions::reject() -{ - accept(); -} - -} // namespace dialogs -} // namespace pv diff --git a/DSView/pv/dialogs/waitingdialog.cpp b/DSView/pv/dialogs/waitingdialog.cpp index 1bd53daa..7996b988 100644 --- a/DSView/pv/dialogs/waitingdialog.cpp +++ b/DSView/pv/dialogs/waitingdialog.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include "libsigrok4DSL/libsigrok.h" #include "../view/trace.h" @@ -43,22 +44,18 @@ const QString WaitingDialog::TIPS_WAIT = tr("Waiting"); const QString WaitingDialog::TIPS_FINISHED = tr("Finished!"); WaitingDialog::WaitingDialog(QWidget *parent, boost::shared_ptr dev_inst) : - QDialog(parent), + DSDialog(parent), _dev_inst(dev_inst), - _button_box(QDialogButtonBox::Save | QDialogButtonBox::Abort, + _button_box(QDialogButtonBox::Abort, Qt::Horizontal, this) { this->setFixedSize((GIF_WIDTH+TIP_WIDTH)*1.2, (GIF_HEIGHT+TIP_HEIGHT)*4); - int midx = this->width() / 2; - int midy = this->height() / 2; - this->setWindowOpacity(0.5); - this->setWindowFlags(Qt::Dialog | Qt::FramelessWindowHint); + this->setWindowOpacity(0.7); label = new QLabel(this); - label->setStyleSheet("background-color: transparent;"); - label->setGeometry(midx-GIF_WIDTH/2, midy-GIF_HEIGHT/2, GIF_WIDTH, GIF_HEIGHT); movie = new QMovie(":/icons/wait.gif"); label->setMovie(movie); + label->setAlignment(Qt::AlignCenter); tips = new QLabel(this); tips->setText(TIPS_WAIT); @@ -66,17 +63,23 @@ WaitingDialog::WaitingDialog(QWidget *parent, boost::shared_ptrsetFont(font); - tips->setGeometry(midx-TIP_WIDTH/2, midy+GIF_HEIGHT/2, TIP_WIDTH, TIP_HEIGHT); + tips->setAlignment(Qt::AlignCenter); index = 0; timer = new QTimer(); connect(timer, SIGNAL(timeout()), this, SLOT(changeText())); - - _button_box.setGeometry(width()-_button_box.width()-30, height()-_button_box.height()-15, - _button_box.width(), _button_box.height()); connect(&_button_box, SIGNAL(accepted()), this, SLOT(accept())); connect(&_button_box, SIGNAL(rejected()), this, SLOT(reject())); - _button_box.buttons().front()->setVisible(false); + connect(_dev_inst.get(), SIGNAL(device_updated()), this, SLOT(stop())); + + + QVBoxLayout *mlayout = new QVBoxLayout(); + mlayout->addWidget(label, Qt::AlignHCenter); + mlayout->addWidget(tips, Qt::AlignHCenter); + mlayout->addWidget(&_button_box); + + layout()->addLayout(mlayout); + setTitle(tr("Zero Adjustment")); } void WaitingDialog::accept() @@ -137,6 +140,15 @@ void WaitingDialog::reject() dlg.exec(); } +void WaitingDialog::stop() +{ + using namespace Qt; + + movie->stop(); + timer->stop(); + QDialog::reject(); +} + void WaitingDialog::start() { movie->start(); @@ -162,7 +174,7 @@ void WaitingDialog::changeText() timer->stop(); tips->setAlignment(Qt::AlignHCenter); tips->setText(TIPS_FINISHED); - _button_box.buttons().front()->setVisible(true); + _button_box.addButton(QDialogButtonBox::Save); } } } else { diff --git a/DSView/pv/dialogs/waitingdialog.h b/DSView/pv/dialogs/waitingdialog.h index 7693dd78..b611c2c4 100644 --- a/DSView/pv/dialogs/waitingdialog.h +++ b/DSView/pv/dialogs/waitingdialog.h @@ -23,7 +23,6 @@ #ifndef DSVIEW_PV_WAITINGDIALOG_H #define DSVIEW_PV_WAITINGDIALOG_H -#include #include #include #include @@ -32,11 +31,13 @@ #include #include +#include "../toolbars/titlebar.h" +#include "dsdialog.h" namespace pv { namespace dialogs { -class WaitingDialog : public QDialog +class WaitingDialog : public DSDialog { Q_OBJECT @@ -60,10 +61,11 @@ protected: private slots: void changeText(); + void stop(); private: boost::shared_ptr _dev_inst; - + toolbars::TitleBar *_titlebar; QDialogButtonBox _button_box; int index; diff --git a/DSView/pv/dock/dsotriggerdock.cpp b/DSView/pv/dock/dsotriggerdock.cpp index f7746d63..38c0542d 100644 --- a/DSView/pv/dock/dsotriggerdock.cpp +++ b/DSView/pv/dock/dsotriggerdock.cpp @@ -23,13 +23,13 @@ #include "dsotriggerdock.h" #include "../sigsession.h" #include "../device/devinst.h" +#include "../dialogs/dsmessagebox.h" #include #include #include #include #include -#include #include #include @@ -167,11 +167,11 @@ void DsoTriggerDock::pos_changed(int pos) SR_CONF_HORIZ_TRIGGERPOS, g_variant_new_byte((uint8_t)pos)); if (!ret) { - QMessageBox msg(this); - msg.setText(tr("Trigger Setting Issue")); - msg.setInformativeText(tr("Change horiz trigger position failed!")); - msg.setStandardButtons(QMessageBox::Ok); - msg.setIcon(QMessageBox::Warning); + dialogs::DSMessageBox msg(this); + msg.mBox()->setText(tr("Trigger Setting Issue")); + msg.mBox()->setInformativeText(tr("Change horiz trigger position failed!")); + msg.mBox()->setStandardButtons(QMessageBox::Ok); + msg.mBox()->setIcon(QMessageBox::Warning); msg.exec(); } @@ -196,11 +196,11 @@ void DsoTriggerDock::hold_changed(int hold) g_variant_new_uint64(holdoff)); if (!ret) { - QMessageBox msg(this); - msg.setText(tr("Trigger Setting Issue")); - msg.setInformativeText(tr("Change trigger hold off time failed!")); - msg.setStandardButtons(QMessageBox::Ok); - msg.setIcon(QMessageBox::Warning); + dialogs::DSMessageBox msg(this); + msg.mBox()->setText(tr("Trigger Setting Issue")); + msg.mBox()->setInformativeText(tr("Change trigger hold off time failed!")); + msg.mBox()->setStandardButtons(QMessageBox::Ok); + msg.mBox()->setIcon(QMessageBox::Warning); msg.exec(); } } @@ -214,11 +214,11 @@ void DsoTriggerDock::source_changed() SR_CONF_TRIGGER_SOURCE, g_variant_new_byte(id)); if (!ret) { - QMessageBox msg(this); - msg.setText(tr("Trigger Setting Issue")); - msg.setInformativeText(tr("Change trigger source failed!")); - msg.setStandardButtons(QMessageBox::Ok); - msg.setIcon(QMessageBox::Warning); + dialogs::DSMessageBox msg(this); + msg.mBox()->setText(tr("Trigger Setting Issue")); + msg.mBox()->setInformativeText(tr("Change trigger source failed!")); + msg.mBox()->setStandardButtons(QMessageBox::Ok); + msg.mBox()->setIcon(QMessageBox::Warning); msg.exec(); } } @@ -232,11 +232,11 @@ void DsoTriggerDock::type_changed() SR_CONF_TRIGGER_SLOPE, g_variant_new_byte(id)); if (!ret) { - QMessageBox msg(this); - msg.setText(tr("Trigger Setting Issue")); - msg.setInformativeText(tr("Change trigger type failed!")); - msg.setStandardButtons(QMessageBox::Ok); - msg.setIcon(QMessageBox::Warning); + dialogs::DSMessageBox msg(this); + msg.mBox()->setText(tr("Trigger Setting Issue")); + msg.mBox()->setInformativeText(tr("Change trigger type failed!")); + msg.mBox()->setStandardButtons(QMessageBox::Ok); + msg.mBox()->setIcon(QMessageBox::Warning); msg.exec(); } } diff --git a/DSView/pv/dock/protocoldock.cpp b/DSView/pv/dock/protocoldock.cpp index 27f83a83..eaabcb8d 100644 --- a/DSView/pv/dock/protocoldock.cpp +++ b/DSView/pv/dock/protocoldock.cpp @@ -28,11 +28,11 @@ #include "../data/decoderstack.h" #include "../dialogs/protocollist.h" #include "../dialogs/protocolexp.h" +#include "../dialogs/dsmessagebox.h" #include #include #include -#include #include #include #include @@ -223,11 +223,11 @@ int ProtocolDock::decoder_name_cmp(const void *a, const void *b) void ProtocolDock::add_protocol() { if (_session.get_device()->dev_inst()->mode != LOGIC) { - QMessageBox msg(this); - msg.setText(tr("Protocol Analyzer")); - msg.setInformativeText(tr("Protocol Analyzer is only valid in Digital Mode!")); - msg.setStandardButtons(QMessageBox::Ok); - msg.setIcon(QMessageBox::Warning); + dialogs::DSMessageBox msg(this); + msg.mBox()->setText(tr("Protocol Analyzer")); + msg.mBox()->setInformativeText(tr("Protocol Analyzer is only valid in Digital Mode!")); + msg.mBox()->setStandardButtons(QMessageBox::Ok); + msg.mBox()->setIcon(QMessageBox::Warning); msg.exec(); } else { srd_decoder *const decoder = @@ -330,11 +330,11 @@ void ProtocolDock::del_protocol() _progress_label_list.clear(); _protocol_index_list.clear(); } else { - QMessageBox msg(this); - msg.setText(tr("Protocol Analyzer")); - msg.setInformativeText(tr("No Protocol Analyzer to delete!")); - msg.setStandardButtons(QMessageBox::Ok); - msg.setIcon(QMessageBox::Warning); + dialogs::DSMessageBox msg(NULL); + msg.mBox()->setText(tr("Protocol Analyzer")); + msg.mBox()->setInformativeText(tr("No Protocol Analyzer to delete!")); + msg.mBox()->setStandardButtons(QMessageBox::Ok); + msg.mBox()->setIcon(QMessageBox::Warning); msg.exec(); } } else { diff --git a/DSView/pv/dock/searchdock.cpp b/DSView/pv/dock/searchdock.cpp index c3801a31..c0b1871b 100644 --- a/DSView/pv/dock/searchdock.cpp +++ b/DSView/pv/dock/searchdock.cpp @@ -30,13 +30,13 @@ #include "../data/snapshot.h" #include "../data/logicsnapshot.h" #include "../device/devinst.h" +#include "../dialogs/dsmessagebox.h" #include #include #include #include #include -#include #include #include @@ -118,31 +118,31 @@ void SearchDock::on_previous() last_pos = _view.get_search_pos(); if (last_pos == 0) { - QMessageBox msg(this); - msg.setText(tr("Search")); - msg.setInformativeText(tr("Search cursor at the start position!")); - msg.setStandardButtons(QMessageBox::Ok); - msg.setIcon(QMessageBox::Warning); + dialogs::DSMessageBox msg(this); + msg.mBox()->setText(tr("Search")); + msg.mBox()->setInformativeText(tr("Search cursor at the start position!")); + msg.mBox()->setStandardButtons(QMessageBox::Ok); + msg.mBox()->setIcon(QMessageBox::Warning); msg.exec(); return; } else { data = (uint8_t*)_session.get_buf(unit_size, length); if (data == NULL) { - QMessageBox msg(this); - msg.setText(tr("Search")); - msg.setInformativeText(tr("No Sample data!")); - msg.setStandardButtons(QMessageBox::Ok); - msg.setIcon(QMessageBox::Warning); + dialogs::DSMessageBox msg(this); + msg.mBox()->setText(tr("Search")); + msg.mBox()->setInformativeText(tr("No Sample data!")); + msg.mBox()->setStandardButtons(QMessageBox::Ok); + msg.mBox()->setIcon(QMessageBox::Warning); msg.exec(); return; } else { const bool ret = search_value(data, unit_size, length, last_pos, 1, value); if (!ret) { - QMessageBox msg(this); - msg.setText(tr("Search")); - msg.setInformativeText(tr("Pattern ") + value + tr(" not found!")); - msg.setStandardButtons(QMessageBox::Ok); - msg.setIcon(QMessageBox::Warning); + dialogs::DSMessageBox msg(this); + msg.mBox()->setText(tr("Search")); + msg.mBox()->setInformativeText(tr("Pattern ") + value + tr(" not found!")); + msg.mBox()->setStandardButtons(QMessageBox::Ok); + msg.mBox()->setIcon(QMessageBox::Warning); msg.exec(); return; } else { @@ -163,30 +163,30 @@ void SearchDock::on_next() last_pos = _view.get_search_pos(); if (last_pos == length - 1) { - QMessageBox msg(this); - msg.setText(tr("Search")); - msg.setInformativeText(tr("Search cursor at the end position!")); - msg.setStandardButtons(QMessageBox::Ok); - msg.setIcon(QMessageBox::Warning); + dialogs::DSMessageBox msg(this); + msg.mBox()->setText(tr("Search")); + msg.mBox()->setInformativeText(tr("Search cursor at the end position!")); + msg.mBox()->setStandardButtons(QMessageBox::Ok); + msg.mBox()->setIcon(QMessageBox::Warning); msg.exec(); return; } else { if (data == NULL) { - QMessageBox msg(this); - msg.setText(tr("Search")); - msg.setInformativeText(tr("No Sample data!")); - msg.setStandardButtons(QMessageBox::Ok); - msg.setIcon(QMessageBox::Warning); + dialogs::DSMessageBox msg(this); + msg.mBox()->setText(tr("Search")); + msg.mBox()->setInformativeText(tr("No Sample data!")); + msg.mBox()->setStandardButtons(QMessageBox::Ok); + msg.mBox()->setIcon(QMessageBox::Warning); msg.exec(); return; } else { const int ret = search_value(data, unit_size, length, last_pos, 0, value); if (!ret) { - QMessageBox msg(this); - msg.setText(tr("Search")); - msg.setInformativeText(tr("Pattern ") + value + tr(" not found!")); - msg.setStandardButtons(QMessageBox::Ok); - msg.setIcon(QMessageBox::Warning); + dialogs::DSMessageBox msg(this); + msg.mBox()->setText(tr("Search")); + msg.mBox()->setInformativeText(tr("Pattern ") + value + tr(" not found!")); + msg.mBox()->setStandardButtons(QMessageBox::Ok); + msg.mBox()->setIcon(QMessageBox::Warning); msg.exec(); return; } else { @@ -198,7 +198,7 @@ void SearchDock::on_next() void SearchDock::on_set() { - dialogs::Search dlg(this, _session.get_device()->dev_inst(), _pattern); + dialogs::Search dlg(this, _session.get_device(), _pattern); if (dlg.exec()) { _pattern = dlg.get_pattern(); _pattern.remove(QChar(' '), Qt::CaseInsensitive); diff --git a/DSView/pv/dock/triggerdock.cpp b/DSView/pv/dock/triggerdock.cpp index 5dcb218a..bef00fd4 100644 --- a/DSView/pv/dock/triggerdock.cpp +++ b/DSView/pv/dock/triggerdock.cpp @@ -23,13 +23,13 @@ #include "triggerdock.h" #include "../sigsession.h" #include "../device/devinst.h" +#include "../dialogs/dsmessagebox.h" #include #include #include #include #include -#include #include #include "libsigrok4DSL/libsigrok.h" @@ -293,22 +293,22 @@ void TriggerDock::adv_trigger() g_variant_unref(gvar); } if (stream) { - QMessageBox msg(this); - msg.setText(tr("Trigger")); - msg.setInformativeText(tr("Stream Mode Don't Support Advanced Trigger!")); - msg.setStandardButtons(QMessageBox::Ok); - msg.setIcon(QMessageBox::Warning); + dialogs::DSMessageBox msg(this); + msg.mBox()->setText(tr("Trigger")); + msg.mBox()->setInformativeText(tr("Stream Mode Don't Support Advanced Trigger!")); + msg.mBox()->setStandardButtons(QMessageBox::Ok); + msg.mBox()->setIcon(QMessageBox::Warning); msg.exec(); simple_radioButton->setChecked(true); } else { widget_enable(0); } } else { - QMessageBox msg(this); - msg.setText(tr("Trigger")); - msg.setInformativeText(tr("Advanced Trigger need DSLogic Hardware Support!")); - msg.setStandardButtons(QMessageBox::Ok); - msg.setIcon(QMessageBox::Warning); + dialogs::DSMessageBox msg(this); + msg.mBox()->setText(tr("Trigger")); + msg.mBox()->setInformativeText(tr("Advanced Trigger need DSLogic Hardware Support!")); + msg.mBox()->setStandardButtons(QMessageBox::Ok); + msg.mBox()->setIcon(QMessageBox::Warning); msg.exec(); simple_radioButton->setChecked(true); } diff --git a/DSView/pv/mainframe.cpp b/DSView/pv/mainframe.cpp new file mode 100644 index 00000000..6049f41a --- /dev/null +++ b/DSView/pv/mainframe.cpp @@ -0,0 +1,307 @@ +/* + * This file is part of the DSView project. + * DSView is based on PulseView. + * + * Copyright (C) 2016 DreamSourceLab + * + * 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 "mainframe.h" + +#include "toolbars/titlebar.h" +#include "mainwindow.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace pv { + +MainFrame::MainFrame(DeviceManager &device_manager, + const char *open_file_name) +{ + setAttribute(Qt::WA_TranslucentBackground); + // Make this a borderless window which can't + // be resized or moved via the window system + setWindowFlags(Qt::FramelessWindowHint); + setMinimumHeight(680); + setMinimumWidth(800); + //resize(1024, 768); + + _moving = false; + _startPos = None; + _freezing = false; + + // MainWindow + _mainWindow = new MainWindow(device_manager, open_file_name, this); + _mainWindow->setWindowFlags(Qt::Widget); + + // Title + _titleBar = new toolbars::TitleBar(true, this); + _titleBar->installEventFilter(this); + _titleBar->setTitle(_mainWindow->windowTitle()); + + QVBoxLayout *vbox = new QVBoxLayout(); + vbox->setMargin(0); + vbox->setSpacing(0); + vbox->addWidget(_titleBar); + vbox->addWidget(_mainWindow); + + _top_left = new widgets::Border (TopLeft, this); + _top_left->setFixedSize(Margin, Margin); + _top_left->installEventFilter(this); + _top = new widgets::Border (Top, this); + _top->setFixedHeight(Margin); + _top->installEventFilter(this); + _top_right = new widgets::Border (TopRight, this); + _top_right->setFixedSize(Margin, Margin); + _top_right->installEventFilter(this); + + _left = new widgets::Border (Left, this); + _left->setFixedWidth(Margin); + _left->installEventFilter(this); + _right = new widgets::Border (Right, this); + _right->setFixedWidth(Margin); + _right->installEventFilter(this); + + _bottom_left = new widgets::Border (BottomLeft, this); + _bottom_left->setFixedSize(Margin, Margin); + _bottom_left->installEventFilter(this); + _bottom = new widgets::Border (Bottom, this); + _bottom->setFixedHeight(Margin); + _bottom->installEventFilter(this); + _bottom_right = new widgets::Border (BottomRight, this); + _bottom_right->setFixedSize(Margin, Margin); + _bottom_right->installEventFilter(this); + + _layout = new QGridLayout(this); + _layout->setMargin(0); + _layout->setSpacing(0); + _layout->addWidget(_top_left, 0, 0); + _layout->addWidget(_top, 0, 1); + _layout->addWidget(_top_right, 0, 2); + _layout->addWidget(_left, 1, 0); + _layout->addLayout(vbox, 1, 1); + _layout->addWidget(_right, 1, 2); + _layout->addWidget(_bottom_left, 2, 0); + _layout->addWidget(_bottom, 2, 1); + _layout->addWidget(_bottom_right, 2, 2); + + connect(&_timer, SIGNAL(timeout()), this, SLOT(unfreezing())); +} + +bool MainFrame::close() +{ + _mainWindow->session_save(); + return QFrame::close(); +} + +void MainFrame::unfreezing() +{ + _freezing = false; +} + +void MainFrame::hide_border() +{ + _top_left->setVisible(false); + _top_right->setVisible(false); + _top->setVisible(false); + _left->setVisible(false); + _right->setVisible(false); + _bottom_left->setVisible(false); + _bottom->setVisible(false); + _bottom_right->setVisible(false); +} + +void MainFrame::show_border() +{ + _top_left->setVisible(true); + _top_right->setVisible(true); + _top->setVisible(true); + _left->setVisible(true); + _right->setVisible(true); + _bottom_left->setVisible(true); + _bottom->setVisible(true); + _bottom_right->setVisible(true); +} + +void MainFrame::showNormal() +{ + show_border(); + QFrame::showNormal(); +} + +void MainFrame::showMaximized() +{ + hide_border(); + QFrame::showMaximized(); +} + +bool MainFrame::eventFilter(QObject *object, QEvent *event) +{ + const QEvent::Type type = event->type(); + const QMouseEvent *const mouse_event = (QMouseEvent*)event; + int newWidth; + int newHeight; + int newLeft; + int newTop; + + if (type == QEvent::MouseMove && !isMaximized()) { + if (!(mouse_event->buttons() || Qt::NoButton)) { + if (object == _top_left) { + _startPos = TopLeft; + setCursor(Qt::SizeFDiagCursor); + } else if (object == _bottom_right) { + _startPos = BottomRight; + setCursor(Qt::SizeFDiagCursor); + } else if (object == _top_right) { + _startPos = TopRight; + setCursor(Qt::SizeBDiagCursor); + } else if (object == _bottom_left) { + _startPos = BottomLeft; + setCursor(Qt::SizeBDiagCursor); + } else if (object == _left) { + _startPos = Left; + setCursor(Qt::SizeHorCursor); + } else if (object == _right) { + _startPos = Right; + setCursor(Qt::SizeHorCursor); + } else if (object == _bottom) { + _startPos = Bottom; + setCursor(Qt::SizeVerCursor); + } else if (object == _top) { + _startPos = Top; + setCursor(Qt::SizeVerCursor); + } else { + _startPos = None; + setCursor(Qt::ArrowCursor); + } + } else if(mouse_event->buttons().testFlag(Qt::LeftButton)) { + if (_moving) { + this->move(mouse_event->globalPos() - _lastMousePosition); + } else if (!_freezing){ + switch (_startPos) { + case TopLeft: + newWidth = std::max(_dragStartGeometry.right() - mouse_event->globalX(), minimumWidth()); + newHeight = std::max(_dragStartGeometry.bottom() - mouse_event->globalY(), minimumHeight()); + newLeft = geometry().left(); + newTop = geometry().top(); + if (newWidth > minimumWidth()) + newLeft = mouse_event->globalX(); + if (newHeight > minimumHeight()) + newTop = mouse_event->globalY(); + setGeometry(newLeft, newTop, + newWidth, newHeight); + break; + case BottomLeft: + newWidth = std::max(_dragStartGeometry.right() - mouse_event->globalX(), minimumWidth()); + newHeight = std::max(mouse_event->globalY() - _dragStartGeometry.top(), minimumHeight()); + newLeft = geometry().left(); + if (newWidth > minimumWidth()) + newLeft = mouse_event->globalX(); + setGeometry(newLeft, _dragStartGeometry.top(), + newWidth, newHeight); + break; + case TopRight: + newWidth = std::max(mouse_event->globalX() - _dragStartGeometry.left(), minimumWidth()); + newHeight = std::max(_dragStartGeometry.bottom() - mouse_event->globalY(), minimumHeight()); + newTop = geometry().top(); + if (newHeight > minimumHeight()) + newTop = mouse_event->globalY(); + setGeometry(_dragStartGeometry.left(), newTop, + newWidth, newHeight); + break; + case BottomRight: + newWidth = std::max(mouse_event->globalX() - _dragStartGeometry.left(), minimumWidth()); + newHeight = std::max(mouse_event->globalY() - _dragStartGeometry.top(), minimumHeight()); + setGeometry(_dragStartGeometry.left(), _dragStartGeometry.top(), + newWidth, newHeight); + break; + case Left: + newWidth = _dragStartGeometry.right() - mouse_event->globalX(); + if (newWidth > minimumWidth()) + setGeometry(mouse_event->globalX(), _dragStartGeometry.top(), + newWidth, height()); + break; + case Right: + newWidth = mouse_event->globalX() - _dragStartGeometry.left(); + if (newWidth > minimumWidth()) + setGeometry(_dragStartGeometry.left(), _dragStartGeometry.top(), + newWidth, height()); + break; + case Top: + newHeight = _dragStartGeometry.bottom() - mouse_event->globalY(); + if (newHeight > minimumHeight()) + setGeometry(_dragStartGeometry.left(), mouse_event->globalY(), + width(), newHeight); + break; + case Bottom: + newHeight = mouse_event->globalY() - _dragStartGeometry.top(); + if (newHeight > minimumHeight()) + setGeometry(_dragStartGeometry.left(), _dragStartGeometry.top(), + width(), newHeight); + break; + default: + break; + } + _freezing = true; + } + return true; + } + } else if (type == QEvent::MouseButtonPress) { + if (mouse_event->button() == Qt::LeftButton) + if (_titleBar->rect().contains(mouse_event->pos()) && + _startPos == None) { + _moving = true; + _lastMousePosition = mouse_event->pos() + + QPoint(Margin, Margin) + + QPoint(geometry().left() - frameGeometry().left(), frameGeometry().right() - geometry().right()); + } + if (_startPos != None) + _draging = true; + _timer.start(50); + _dragStartGeometry = geometry(); + } else if (type == QEvent::MouseButtonRelease) { + if (mouse_event->button() == Qt::LeftButton) { + _moving = false; + _draging = false; + _timer.stop(); + } + } else if (!_draging && type == QEvent::Leave) { + _startPos = None; + setCursor(Qt::ArrowCursor); + } else if (type == QEvent::Resize) { + if (isMaximized()) { + hide_border(); + } else { + show_border(); + } + _titleBar->setRestoreButton(isMaximized()); + _layout->update(); + } + + return QObject::eventFilter(object, event); +} + +} // namespace pv diff --git a/DSView/pv/mainframe.h b/DSView/pv/mainframe.h new file mode 100644 index 00000000..eafc317c --- /dev/null +++ b/DSView/pv/mainframe.h @@ -0,0 +1,103 @@ +/* + * This file is part of the DSView project. + * DSView is based on PulseView. + * + * Copyright (C) 2016 DreamSourceLab + * + * 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 + */ + + +#ifndef DSVIEW_PV_MAINFRAME_H +#define DSVIEW_PV_MAINFRAME_H + +#include "widgets/border.h" + +#include +#include +#include + +namespace pv { + +class DeviceManager; +class MainWindow; + +namespace toolbars { +class TitleBar; +} + +class MainFrame : public QFrame +{ + Q_OBJECT + +public: + static const int Margin = 8; + + enum borderTypes{ + None, + TopLeft, + Left, + BottomLeft, + Bottom, + BottomRight, + Right, + TopRight, + Top + }borderTypes; + +public: + MainFrame(DeviceManager &device_manager, + const char *open_file_name = NULL); + + void showMaxRestore(); + +protected: + bool eventFilter(QObject *object, QEvent *event); + +public slots: + void unfreezing(); + bool close(); + void showNormal(); + void showMaximized(); +private: + void hide_border(); + void show_border(); + +private: + toolbars::TitleBar *_titleBar; + MainWindow *_mainWindow; + + QGridLayout *_layout; + widgets::Border *_left; + widgets::Border *_right; + widgets::Border *_top; + widgets::Border *_bottom; + widgets::Border *_top_left; + widgets::Border *_top_right; + widgets::Border *_bottom_left; + widgets::Border *_bottom_right; + + bool _moving; + bool _draging; + QPoint _lastMousePosition; + QRect _dragStartGeometry; + int _startPos; + QTimer _timer; + bool _freezing; +}; + +} // namespace pv + +#endif // DSVIEW_PV_MAINFRAME_H diff --git a/DSView/pv/mainwindow.cpp b/DSView/pv/mainwindow.cpp index 5dc78610..ca9825e4 100644 --- a/DSView/pv/mainwindow.cpp +++ b/DSView/pv/mainwindow.cpp @@ -56,11 +56,13 @@ #include "dialogs/deviceoptions.h" #include "dialogs/storeprogress.h" #include "dialogs/waitingdialog.h" +#include "dialogs/dsmessagebox.h" #include "toolbars/samplingbar.h" #include "toolbars/trigbar.h" #include "toolbars/filebar.h" #include "toolbars/logobar.h" +#include "toolbars/titlebar.h" #include "dock/triggerdock.h" #include "dock/dsotriggerdock.h" @@ -87,8 +89,6 @@ using boost::dynamic_pointer_cast; using std::list; using std::vector; -extern char AppDataPath[256]; - namespace pv { MainWindow::MainWindow(DeviceManager &device_manager, @@ -113,9 +113,8 @@ MainWindow::MainWindow(DeviceManager &device_manager, void MainWindow::setup_ui() { setObjectName(QString::fromUtf8("MainWindow")); - setMinimumHeight(680); - setMinimumWidth(800); - resize(1024, 768); + layout()->setMargin(0); + layout()->setSpacing(0); // Set the window icon QIcon icon; @@ -126,7 +125,7 @@ void MainWindow::setup_ui() // Setup the central widget _central_widget = new QWidget(this); _vertical_layout = new QVBoxLayout(_central_widget); - _vertical_layout->setSpacing(6); + _vertical_layout->setSpacing(0); _vertical_layout->setContentsMargins(0, 0, 0, 0); setCentralWidget(_central_widget); @@ -356,16 +355,17 @@ void MainWindow::load_file(QString file_name) void MainWindow::show_session_error( const QString text, const QString info_text) { - QMessageBox msg(this); - msg.setText(text); - msg.setInformativeText(info_text); - msg.setStandardButtons(QMessageBox::Ok); - msg.setIcon(QMessageBox::Warning); + dialogs::DSMessageBox msg(this); + msg.mBox()->setText(text); + msg.mBox()->setInformativeText(info_text); + msg.mBox()->setStandardButtons(QMessageBox::Ok); + msg.mBox()->setIcon(QMessageBox::Warning); msg.exec(); } void MainWindow::device_attach() { + _session.get_device()->device_updated(); //_session.stop_hot_plug_proc(); if (_session.get_capture_state() == SigSession::Running) @@ -384,12 +384,14 @@ void MainWindow::device_attach() void MainWindow::device_detach() { + _session.get_device()->device_updated(); //_session.stop_hot_plug_proc(); if (_session.get_capture_state() == SigSession::Running) _session.stop_capture(); session_save(); + _view->hide_calibration(); struct sr_dev_driver **const drivers = sr_driver_list(); struct sr_dev_driver **driver; @@ -453,33 +455,33 @@ void MainWindow::test_data_error() { _session.stop_capture(); - QMessageBox msg(this); - msg.setText(tr("Data Error")); - msg.setInformativeText(tr("the receive data are not consist with pre-defined test data")); - msg.setStandardButtons(QMessageBox::Ok); - msg.setIcon(QMessageBox::Warning); + dialogs::DSMessageBox msg(this); + msg.mBox()->setText(tr("Data Error")); + msg.mBox()->setInformativeText(tr("the receive data are not consist with pre-defined test data")); + msg.mBox()->setStandardButtons(QMessageBox::Ok); + msg.mBox()->setIcon(QMessageBox::Warning); msg.exec(); } void MainWindow::malloc_error() { _session.stop_capture(); - QMessageBox msg(this); - msg.setText(tr("Malloc Error")); - msg.setInformativeText(tr("Memory is not enough for this sample!\nPlease reduce the sample depth!")); - msg.setStandardButtons(QMessageBox::Ok); - msg.setIcon(QMessageBox::Warning); + dialogs::DSMessageBox msg(this); + msg.mBox()->setText(tr("Malloc Error")); + msg.mBox()->setInformativeText(tr("Memory is not enough for this sample!\nPlease reduce the sample depth!")); + msg.mBox()->setStandardButtons(QMessageBox::Ok); + msg.mBox()->setIcon(QMessageBox::Warning); msg.exec(); } void MainWindow::hardware_connect_failed() { _session.stop_capture(); - QMessageBox msg(this); - msg.setText(tr("Hardware Connect Failed")); - msg.setInformativeText(tr("Please check hardware connection!")); - msg.setStandardButtons(QMessageBox::Ok); - msg.setIcon(QMessageBox::Warning); + dialogs::DSMessageBox msg(this); + msg.mBox()->setText(tr("Hardware Connect Failed")); + msg.mBox()->setInformativeText(tr("Please check hardware connection!")); + msg.mBox()->setStandardButtons(QMessageBox::Ok); + msg.mBox()->setIcon(QMessageBox::Warning); msg.exec(); } @@ -606,11 +608,11 @@ bool MainWindow::load_session(QString name) { QFile sessionFile(name); if (!sessionFile.open(QIODevice::ReadOnly)) { - QMessageBox msg(this); - msg.setText(tr("File Error")); - msg.setInformativeText(tr("Couldn't open session file!")); - msg.setStandardButtons(QMessageBox::Ok); - msg.setIcon(QMessageBox::Warning); + dialogs::DSMessageBox msg(this); + msg.mBox()->setText(tr("File Error")); + msg.mBox()->setInformativeText(tr("Couldn't open session file!")); + msg.mBox()->setStandardButtons(QMessageBox::Ok); + msg.mBox()->setIcon(QMessageBox::Warning); msg.exec(); return false; } @@ -623,11 +625,11 @@ bool MainWindow::load_session(QString name) const sr_dev_inst *const sdi = _session.get_device()->dev_inst(); if (strcmp(sdi->driver->name, sessionObj["Device"].toString().toLocal8Bit()) != 0 || sdi->mode != sessionObj["DeviceMode"].toDouble()) { - QMessageBox msg(this); - msg.setText(tr("Session Error")); - msg.setInformativeText(tr("Session File is not compatible with current device or mode!")); - msg.setStandardButtons(QMessageBox::Ok); - msg.setIcon(QMessageBox::Warning); + dialogs::DSMessageBox msg(this); + msg.mBox()->setText(tr("Session Error")); + msg.mBox()->setInformativeText(tr("Session File is not compatible with current device or mode!")); + msg.mBox()->setStandardButtons(QMessageBox::Ok); + msg.mBox()->setIcon(QMessageBox::Warning); msg.exec(); return false; } @@ -719,11 +721,11 @@ bool MainWindow::store_session(QString name) { QFile sessionFile(name); if (!sessionFile.open(QIODevice::WriteOnly | QIODevice::Text)) { - QMessageBox msg(this); - msg.setText(tr("File Error")); - msg.setInformativeText(tr("Couldn't open session file to write!")); - msg.setStandardButtons(QMessageBox::Ok); - msg.setIcon(QMessageBox::Warning); + dialogs::DSMessageBox msg(this); + msg.mBox()->setText(tr("File Error")); + msg.mBox()->setInformativeText(tr("Couldn't open session file to write!")); + msg.mBox()->setStandardButtons(QMessageBox::Ok); + msg.mBox()->setIcon(QMessageBox::Warning); msg.exec(); return false; } @@ -805,10 +807,8 @@ bool MainWindow::eventFilter(QObject *object, QEvent *event) { (void) object; - if( event->type() == QEvent::KeyPress ) { - const vector< shared_ptr > sigs( - _session.get_signals()); - + if ( event->type() == QEvent::KeyPress ) { + const vector< shared_ptr > sigs(_session.get_signals()); QKeyEvent *ke = (QKeyEvent *) event; switch(ke->key()) { case Qt::Key_S: diff --git a/DSView/pv/mainwindow.h b/DSView/pv/mainwindow.h index 5159d077..f6cdb0b6 100644 --- a/DSView/pv/mainwindow.h +++ b/DSView/pv/mainwindow.h @@ -76,6 +76,8 @@ public: const char *open_file_name = NULL, QWidget *parent = 0); + void session_save(); + protected: void closeEvent(QCloseEvent *event); @@ -86,8 +88,6 @@ private: bool eventFilter(QObject *object, QEvent *event); - void session_save(); - private slots: void load_file(QString file_name); diff --git a/DSView/pv/sigsession.cpp b/DSView/pv/sigsession.cpp index 3abb5eac..1c9e7f1e 100644 --- a/DSView/pv/sigsession.cpp +++ b/DSView/pv/sigsession.cpp @@ -56,11 +56,11 @@ #include #include -#include #include #include #include #include +#include #include #include @@ -194,7 +194,7 @@ void SigSession::set_file(QString name) throw(QString) } } -void SigSession::save_file(const QString name, int type){ +void SigSession::save_file(const QString name, QWidget* parent, int type){ unsigned char* data; int unit_size; uint64_t sample_count; @@ -231,8 +231,23 @@ void SigSession::save_file(const QString name, int type){ sample_count = snapshot->get_sample_count(); } - sr_session_save(name.toLocal8Bit().data(), _dev_inst->dev_inst(), - data, unit_size, sample_count, _trigger_time.toMSecsSinceEpoch(), _trigger_pos); + QFuture future; + future = QtConcurrent::run([&]{ + sr_session_save(name.toLocal8Bit().data(), _dev_inst->dev_inst(), + data, unit_size, sample_count, _trigger_time.toMSecsSinceEpoch(), _trigger_pos); + }); + Qt::WindowFlags flags = Qt::CustomizeWindowHint; + QProgressDialog dlg(tr("Save Capture to File... It can take a while."), + tr("Cancel"),0,0,parent,flags); + dlg.setWindowModality(Qt::WindowModal); + dlg.setWindowFlags(Qt::Dialog | Qt::FramelessWindowHint); + dlg.setCancelButton(NULL); + + QFutureWatcher watcher; + watcher.setFuture(future); + connect(&watcher,SIGNAL(finished()),&dlg,SLOT(cancel())); + + dlg.exec(); } QList SigSession::getSuportedExportFormats(){ diff --git a/DSView/pv/sigsession.h b/DSView/pv/sigsession.h index 5a9d8fa8..d046cbde 100644 --- a/DSView/pv/sigsession.h +++ b/DSView/pv/sigsession.h @@ -119,7 +119,7 @@ public: void set_file(QString name) throw(QString); - void save_file(const QString name, int type); + void save_file(const QString name, QWidget* parent, int type); void set_default_device(boost::function error_handler); void export_file(const QString name, QWidget* parent, const QString ext); diff --git a/DSView/pv/toolbars/filebar.cpp b/DSView/pv/toolbars/filebar.cpp index 6007ad66..9f10daa6 100644 --- a/DSView/pv/toolbars/filebar.cpp +++ b/DSView/pv/toolbars/filebar.cpp @@ -25,17 +25,15 @@ #include #include -#include #include #include #include "filebar.h" #include "../device/devinst.h" +#include "../dialogs/dsmessagebox.h" #include -extern char AppDataPath[256]; - namespace pv { namespace toolbars { @@ -144,11 +142,11 @@ void FileBar::session_error( void FileBar::show_session_error( const QString text, const QString info_text) { - QMessageBox msg(this); - msg.setText(text); - msg.setInformativeText(info_text); - msg.setStandardButtons(QMessageBox::Ok); - msg.setIcon(QMessageBox::Warning); + dialogs::DSMessageBox msg(this); + msg.mBox()->setText(text); + msg.mBox()->setInformativeText(info_text); + msg.mBox()->setStandardButtons(QMessageBox::Ok); + msg.mBox()->setIcon(QMessageBox::Warning); msg.exec(); } @@ -157,11 +155,11 @@ void FileBar::on_actionExport_triggered(){ uint64_t length; const void* buf = _session.get_buf(unit_size, length); if (!buf) { - QMessageBox msg(this); - msg.setText(tr("Data Export")); - msg.setInformativeText(tr("No Data to Save!")); - msg.setStandardButtons(QMessageBox::Ok); - msg.setIcon(QMessageBox::Warning); + dialogs::DSMessageBox msg(this); + msg.mBox()->setText(tr("Data Export")); + msg.mBox()->setInformativeText(tr("No Data to Save!")); + msg.mBox()->setStandardButtons(QMessageBox::Ok); + msg.mBox()->setIcon(QMessageBox::Warning); msg.exec(); } else { QList supportedFormats = _session.getSuportedExportFormats(); @@ -191,11 +189,11 @@ void FileBar::on_actionSave_triggered() uint64_t length; const void* buf = _session.get_buf(unit_size, length); if (!buf) { - QMessageBox msg(this); - msg.setText(tr("File Save")); - msg.setInformativeText(tr("No Data to Save!")); - msg.setStandardButtons(QMessageBox::Ok); - msg.setIcon(QMessageBox::Warning); + dialogs::DSMessageBox msg(this); + msg.mBox()->setText(tr("File Save")); + msg.mBox()->setInformativeText(tr("No Data to Save!")); + msg.mBox()->setStandardButtons(QMessageBox::Ok); + msg.mBox()->setIcon(QMessageBox::Warning); msg.exec(); } else { QString file_name = QFileDialog::getSaveFileName( @@ -206,7 +204,7 @@ void FileBar::on_actionSave_triggered() QFileInfo f(file_name); if(f.suffix().compare("dsl")) file_name.append(tr(".dsl")); - _session.save_file(file_name, _session.get_device()->dev_inst()->mode); + _session.save_file(file_name, this, _session.get_device()->dev_inst()->mode); } } } @@ -226,11 +224,11 @@ void FileBar::on_actionDefault_triggered() { QDir dir(DS_RES_PATH); if (!dir.exists()) { - QMessageBox msg(this); - msg.setText(tr("Session Load")); - msg.setInformativeText(tr("Cannot find default session file for this device!")); - msg.setStandardButtons(QMessageBox::Ok); - msg.setIcon(QMessageBox::Warning); + dialogs::DSMessageBox msg(this); + msg.mBox()->setText(tr("Session Load")); + msg.mBox()->setInformativeText(tr("Cannot find default session file for this device!")); + msg.mBox()->setStandardButtons(QMessageBox::Ok); + msg.mBox()->setIcon(QMessageBox::Warning); msg.exec(); return; } diff --git a/DSView/pv/toolbars/logobar.cpp b/DSView/pv/toolbars/logobar.cpp index 8f19e614..91d52539 100644 --- a/DSView/pv/toolbars/logobar.cpp +++ b/DSView/pv/toolbars/logobar.cpp @@ -24,7 +24,6 @@ #include #include -#include #include #include #include @@ -32,6 +31,7 @@ #include "logobar.h" #include "../dialogs/about.h" +#include "../dialogs/dsmessagebox.h" namespace pv { namespace toolbars { @@ -93,11 +93,11 @@ void LogoBar::session_error( void LogoBar::show_session_error( const QString text, const QString info_text) { - QMessageBox msg(this); - msg.setText(text); - msg.setInformativeText(info_text); - msg.setStandardButtons(QMessageBox::Ok); - msg.setIcon(QMessageBox::Warning); + dialogs::DSMessageBox msg(this); + msg.mBox()->setText(text); + msg.mBox()->setInformativeText(info_text); + msg.mBox()->setStandardButtons(QMessageBox::Ok); + msg.mBox()->setIcon(QMessageBox::Warning); msg.exec(); } diff --git a/DSView/pv/toolbars/samplingbar.cpp b/DSView/pv/toolbars/samplingbar.cpp index 15cf2c81..b2ca6c0c 100644 --- a/DSView/pv/toolbars/samplingbar.cpp +++ b/DSView/pv/toolbars/samplingbar.cpp @@ -31,7 +31,6 @@ #include #include #include -#include #include "samplingbar.h" @@ -39,7 +38,7 @@ #include "../device/devinst.h" #include "../dialogs/deviceoptions.h" #include "../dialogs/waitingdialog.h" -#include "../dialogs/streamoptions.h" +#include "../dialogs/dsmessagebox.h" #include "../view/dsosignal.h" using namespace boost; @@ -97,6 +96,8 @@ SamplingBar::SamplingBar(SigSession &session, QWidget *parent) : _instant(false) { setMovable(false); + layout()->setMargin(0); + layout()->setSpacing(0); connect(&_device_selector, SIGNAL(currentIndexChanged (int)), this, SLOT(on_device_selected())); @@ -124,7 +125,9 @@ SamplingBar::SamplingBar(SigSession &session, QWidget *parent) : connect(&_sample_rate, SIGNAL(currentIndexChanged(int)), this, SLOT(on_samplerate_sel(int))); - addWidget(new QLabel(tr(" "))); + QWidget *leftMargin = new QWidget(this); + leftMargin->setFixedWidth(4); + addWidget(leftMargin); addWidget(&_device_selector); addWidget(&_configure_button); addWidget(&_sample_count); @@ -211,8 +214,10 @@ void SamplingBar::on_configure() if (gvar != NULL) { bool zero = g_variant_get_boolean(gvar); g_variant_unref(gvar); - if (zero) + if (zero) { zero_adj(); + return; + } } gvar = dev_inst->get_config(NULL, NULL, SR_CONF_CALI); @@ -221,25 +226,25 @@ void SamplingBar::on_configure() g_variant_unref(gvar); if (cali) { show_calibration(); + return; } } } - } - - GVariant* gvar = dev_inst->get_config(NULL, NULL, SR_CONF_TEST); - if (gvar != NULL) { - bool test = g_variant_get_boolean(gvar); - g_variant_unref(gvar); - if (test) { - update_sample_count_selector_value(); - update_sample_rate_selector_value(); - #ifndef TEST_MODE - _sample_count.setDisabled(true); - _sample_rate.setDisabled(true); - #endif - } else if (dev_inst->dev_inst()->mode != DSO) { - _sample_count.setDisabled(false); - _sample_rate.setDisabled(false); + gvar = dev_inst->get_config(NULL, NULL, SR_CONF_TEST); + if (gvar != NULL) { + bool test = g_variant_get_boolean(gvar); + g_variant_unref(gvar); + if (test) { + update_sample_count_selector_value(); + update_sample_rate_selector_value(); + #ifndef TEST_MODE + _sample_count.setDisabled(true); + _sample_rate.setDisabled(true); + #endif + } else if (dev_inst->dev_inst()->mode != DSO) { + _sample_count.setDisabled(false); + _sample_rate.setDisabled(false); + } } } } @@ -257,7 +262,8 @@ void SamplingBar::zero_adj() pv::dialogs::WaitingDialog wait(this, get_selected_device()); wait.start(); - run_stop(); + if (_session.get_capture_state() == pv::SigSession::Running) + run_stop(); } uint64_t SamplingBar::get_record_length() const @@ -649,15 +655,14 @@ void SamplingBar::on_run_stop() bool zero = g_variant_get_boolean(gvar); g_variant_unref(gvar); if (zero) { - QMessageBox msg(this); - msg.setText(tr("Zero Adjustment")); - msg.setInformativeText(tr("Please adjust zero skew and save the result!")); + dialogs::DSMessageBox msg(this); + msg.mBox()->setText(tr("Zero Adjustment")); + msg.mBox()->setInformativeText(tr("Please adjust zero skew and save the result!")); //msg.setStandardButtons(QMessageBox::Ok); - msg.addButton(tr("Ok"), QMessageBox::AcceptRole); - msg.addButton(tr("Skip"), QMessageBox::RejectRole); - msg.setIcon(QMessageBox::Warning); - int ret = msg.exec(); - if ( ret == QMessageBox::AcceptRole) { + msg.mBox()->addButton(tr("Ok"), QMessageBox::AcceptRole); + msg.mBox()->addButton(tr("Skip"), QMessageBox::RejectRole); + msg.mBox()->setIcon(QMessageBox::Warning); + if (msg.exec()) { zero_adj(); } else { dev_inst->set_config(NULL, NULL, SR_CONF_ZERO, g_variant_new_boolean(false)); @@ -687,15 +692,14 @@ void SamplingBar::on_instant_stop() bool zero = g_variant_get_boolean(gvar); g_variant_unref(gvar); if (zero) { - QMessageBox msg(this); - msg.setText(tr("Zero Adjustment")); - msg.setInformativeText(tr("Zero adjustment program will be started. Please keep all channels out of singal input. It can take a while!")); - //msg.setStandardButtons(QMessageBox::Ok); - msg.addButton(tr("Ok"), QMessageBox::AcceptRole); - msg.addButton(tr("Skip"), QMessageBox::RejectRole); - msg.setIcon(QMessageBox::Warning); - int ret = msg.exec(); - if ( ret == QMessageBox::AcceptRole) { + dialogs::DSMessageBox msg(this); + msg.mBox()->setText(tr("Zero Adjustment")); + msg.mBox()->setInformativeText(tr("Zero adjustment program will be started. Please keep all channels out of singal input. It can take a while!")); + //msg.mBox()->setStandardButtons(QMessageBox::Ok); + msg.mBox()->addButton(tr("Ok"), QMessageBox::AcceptRole); + msg.mBox()->addButton(tr("Skip"), QMessageBox::RejectRole); + msg.mBox()->setIcon(QMessageBox::Warning); + if (msg.exec()) { zero_adj(); } else { dev_inst->set_config(NULL, NULL, SR_CONF_ZERO, g_variant_new_boolean(false)); @@ -758,11 +762,11 @@ void SamplingBar::enable_instant(bool enable) void SamplingBar::show_session_error( const QString text, const QString info_text) { - QMessageBox msg(this); - msg.setText(text); - msg.setInformativeText(info_text); - msg.setStandardButtons(QMessageBox::Ok); - msg.setIcon(QMessageBox::Warning); + dialogs::DSMessageBox msg(this); + msg.mBox()->setText(text); + msg.mBox()->setInformativeText(info_text); + msg.mBox()->setStandardButtons(QMessageBox::Ok); + msg.mBox()->setIcon(QMessageBox::Warning); msg.exec(); } diff --git a/DSView/pv/toolbars/titlebar.cpp b/DSView/pv/toolbars/titlebar.cpp new file mode 100644 index 00000000..942a3fd8 --- /dev/null +++ b/DSView/pv/toolbars/titlebar.cpp @@ -0,0 +1,189 @@ +/* + * This file is part of the DSView project. + * DSView is based on PulseView. + * + * Copyright (C) 2016 DreamSourceLab + * + * 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 "titlebar.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef Q_OS_WIN +#pragma comment(lib, "user32.lib") +#include +#endif + +namespace pv { +namespace toolbars { + +TitleBar::TitleBar(bool top, QWidget *parent, bool hasClose) : + _moving(false), + _isTop(top), + _hasClose(hasClose), + QWidget(parent) +{ + setObjectName("TitleBar"); + setFixedHeight(28); + + _title = new QLabel(this); + QHBoxLayout *hbox = new QHBoxLayout(this); + hbox->addWidget(_title); + + if (_isTop) { + _minimizeButton = new QToolButton(this); + _minimizeButton->setObjectName("MinimizeButton"); + _minimizeButton->setIcon(QIcon::fromTheme("titlebar", + QIcon(":/icons/minimize.png"))); + _maximizeButton = new QToolButton(this); + _maximizeButton->setObjectName("MaximizeButton"); + _maximizeButton->setIcon(QIcon::fromTheme("titlebar", + QIcon(":/icons/maximize.png"))); + + hbox->addWidget(_minimizeButton); + hbox->addWidget(_maximizeButton); + + connect(this, SIGNAL( normalShow() ), parent, SLOT(showNormal() ) ); + connect(this, SIGNAL( maximizedShow() ), parent, SLOT(showMaximized() ) ); + connect(_minimizeButton, SIGNAL( clicked() ), this, SLOT(showSmall() ) ); + connect(_maximizeButton, SIGNAL( clicked() ), this, SLOT(showMaxRestore() ) ); + } + + if (_isTop || _hasClose) { + _closeButton= new QToolButton(this); + _closeButton->setObjectName("CloseButton"); + _closeButton->setIcon(QIcon::fromTheme("titlebar", + QIcon(":/icons/close.png"))); + hbox->addWidget(_closeButton); + connect(_closeButton, SIGNAL( clicked() ), parent, SLOT(close() ) ); + } + + hbox->insertStretch(0, 500); + hbox->insertStretch(2, 500); + hbox->setMargin(0); + hbox->setSpacing(0); + setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); +} + +void TitleBar::paintEvent(QPaintEvent *) +{ + QPainter p(this); + p.setRenderHint(QPainter::Antialiasing, true); + p.setPen(QColor(48, 47, 47, 255)); + p.setBrush(QColor(48, 47, 47, 255)); + p.drawRect(rect()); + + const int xgap = 2.5; + const int xstart = 10; + p.setPen(QPen(QColor(213, 15, 37, 255), 2, Qt::SolidLine)); + p.drawLine(xstart + xgap*0, height()*0.50, xstart + xgap*0, height()*0.66); + p.drawLine(xstart + xgap*18, height()*0.34, xstart + xgap*18, height()*0.50); + + p.setPen(QPen(QColor(238, 178, 17, 255), 2, Qt::SolidLine)); + p.drawLine(xstart + xgap*2, height()*0.50, xstart + xgap*2, height()*0.83); + p.drawLine(xstart + xgap*16, height()*0.17, xstart + xgap*16, height()*0.50); + + p.setPen(QPen(QColor(17, 133, 209, 255), 2, Qt::SolidLine)); + p.drawLine(xstart + xgap*4, height()*0.50, xstart + xgap*4, height()*1.00); + p.drawLine(xstart + xgap*14, height()*0.00, xstart + xgap*14, height()*0.50); + + p.setPen(QPen(QColor(0, 153, 37, 200), 2, Qt::SolidLine)); + p.drawLine(xstart + xgap*6, height()*0.50, xstart + xgap*6, height()*0.83); + p.drawLine(xstart + xgap*12, height()*0.17, xstart + xgap*12, height()*0.50); + + p.setPen(QPen(QColor(109, 50, 156, 255), 2, Qt::SolidLine)); + p.drawLine(xstart + xgap*8, height()*0.50, xstart + xgap*8, height()*0.66); + p.drawLine(xstart + xgap*10, height()*0.34, xstart + xgap*10, height()*0.50); +} + +void TitleBar::setTitle(QString title) +{ + _title->setText(title); +} + +QPoint TitleBar::get_startPos() const +{ + return _startPos; +} + +QString TitleBar::title() const +{ + return _title->text(); +} + +void TitleBar::showMaxRestore() +{ + if (parentWidget()->isMaximized()) { + _maximizeButton->setIcon(QIcon::fromTheme("titlebar", + QIcon(":/icons/maximize.png"))); + normalShow(); + } else { + _maximizeButton->setIcon(QIcon::fromTheme("titlebar", + QIcon(":/icons/restore.png"))); + maximizedShow(); + } +} + +void TitleBar::setRestoreButton(bool max) +{ + if (!max) { + _maximizeButton->setIcon(QIcon::fromTheme("titlebar", + QIcon(":/icons/maximize.png"))); + } else { + _maximizeButton->setIcon(QIcon::fromTheme("titlebar", + QIcon(":/icons/restore.png"))); + } +} + +void TitleBar::mousePressEvent(QMouseEvent* event) +{ + if(event->button() == Qt::LeftButton && !parentWidget()->isMaximized()) { + _moving = true; + _startPos = mapToParent(event->pos()); + } +} + +void TitleBar::mouseMoveEvent(QMouseEvent *event) +{ + if(_moving && event->buttons().testFlag(Qt::LeftButton)) { + parentWidget()->move(event->globalPos() - _startPos); + } +} + +void TitleBar::mouseReleaseEvent(QMouseEvent* event) +{ + if(event->button() == Qt::LeftButton) { + _moving = false; + } +} + +void TitleBar::mouseDoubleClickEvent(QMouseEvent *event) +{ + if (_isTop) + showMaxRestore(); + QWidget::mouseDoubleClickEvent(event); +} + +} // namespace toolbars +} // namespace pv diff --git a/DSView/pv/toolbars/titlebar.h b/DSView/pv/toolbars/titlebar.h new file mode 100644 index 00000000..d0c785d7 --- /dev/null +++ b/DSView/pv/toolbars/titlebar.h @@ -0,0 +1,72 @@ +/* + * This file is part of the DSView project. + * DSView is based on PulseView. + * + * Copyright (C) 2016 DreamSourceLab + * + * 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 + */ + +#ifndef DSVIEW_PV_TOOLBARS_TITLEBAR_H +#define DSVIEW_PV_TOOLBARS_TITLEBAR_H + +#include +class QLabel; +class QToolButton; + +namespace pv { +namespace toolbars { + +class TitleBar : public QWidget +{ + Q_OBJECT + +public: + TitleBar(bool top, QWidget *parent, bool hasClose = false); + void setTitle(QString title); + QPoint get_startPos() const; + QString title() const; + +signals: + void normalShow(); + void maximizedShow(); + +public slots: + void showSmall() { parentWidget()->showMinimized(); } + void showMaxRestore(); + void setRestoreButton(bool max); + +protected: + void paintEvent(QPaintEvent *); + void mousePressEvent(QMouseEvent *event); + void mouseMoveEvent(QMouseEvent *event); + void mouseReleaseEvent(QMouseEvent *event); + void mouseDoubleClickEvent(QMouseEvent *event); + + QLabel *_title; + QToolButton *_minimizeButton; + QToolButton *_maximizeButton; + QToolButton *_closeButton; + + bool _moving; + bool _isTop; + bool _hasClose; + QPoint _startPos; +}; + +} // namespace toolbars +} // namespace pv + +#endif // DSVIEW_PV_TOOLBARS_TITLEBAR_H diff --git a/DSView/pv/view/decodetrace.cpp b/DSView/pv/view/decodetrace.cpp index 2cce5a7e..1887de34 100644 --- a/DSView/pv/view/decodetrace.cpp +++ b/DSView/pv/view/decodetrace.cpp @@ -52,6 +52,7 @@ extern "C" { #include "../widgets/decodermenu.h" #include "../device/devinst.h" #include "../view/cursor.h" +#include "../toolbars/titlebar.h" using namespace boost; using namespace std; @@ -380,7 +381,8 @@ void DecodeTrace::paint_fore(QPainter &p, int left, int right) bool DecodeTrace::create_popup() { int ret = false; - _popup = new QDialog(); + _popup = new dialogs::DSDialog(); + create_popup_form(); if (QDialog::Accepted == _popup->exec()) @@ -397,8 +399,7 @@ bool DecodeTrace::create_popup() } } - if (_popup_form) - QWidget().setLayout(_popup_form); + delete _popup_form; delete _popup; _popup = NULL; _popup_form = NULL; @@ -414,14 +415,13 @@ void DecodeTrace::create_popup_form() // which then goes out of scope destroying the layout and all the child // widgets. if (_popup_form) - QWidget().setLayout(_popup_form); + _popup->reload(false); + + _popup_form = new QFormLayout(); + _popup->layout()->addLayout(_popup_form); + _popup->setTitle(tr("Decoder Options")); - _popup_form = new QFormLayout(_popup); - _popup->setLayout(_popup_form); populate_popup_form(_popup, _popup_form); - const int width = _popup_form->sizeHint().width(); - const int height = _popup_form->sizeHint().height(); - _popup->resize(width, height); } void DecodeTrace::populate_popup_form(QWidget *parent, QFormLayout *form) diff --git a/DSView/pv/view/decodetrace.h b/DSView/pv/view/decodetrace.h index ff65a1a8..9ee89d84 100644 --- a/DSView/pv/view/decodetrace.h +++ b/DSView/pv/view/decodetrace.h @@ -29,11 +29,11 @@ #include #include -#include #include #include +#include "../dialogs/dsdialog.h" struct srd_channel; struct srd_decoder; @@ -224,7 +224,7 @@ private: std::vector _cur_row_headings; QFormLayout *_popup_form; - QDialog *_popup; + dialogs::DSDialog *_popup; }; } // namespace view diff --git a/DSView/pv/view/devmode.cpp b/DSView/pv/view/devmode.cpp index b9283f2b..f28554aa 100644 --- a/DSView/pv/view/devmode.cpp +++ b/DSView/pv/view/devmode.cpp @@ -35,7 +35,6 @@ #include #include #include -#include #include using boost::shared_ptr; @@ -50,6 +49,8 @@ DevMode::DevMode(QWidget *parent, SigSession &session) : _layout(new QGridLayout(this)) { + _layout->setMargin(5); + _layout->setSpacing(0); setLayout(_layout); } diff --git a/DSView/pv/view/header.cpp b/DSView/pv/view/header.cpp index 3b46006b..b358c202 100644 --- a/DSView/pv/view/header.cpp +++ b/DSView/pv/view/header.cpp @@ -46,7 +46,6 @@ #include #include #include -#include using namespace boost; using namespace std; diff --git a/DSView/pv/view/signal.cpp b/DSView/pv/view/signal.cpp index 40c66101..b61b8d70 100644 --- a/DSView/pv/view/signal.cpp +++ b/DSView/pv/view/signal.cpp @@ -67,5 +67,10 @@ void Signal::paint_axis(QPainter &p, int y, int left, int right) p.drawLine(QPointF(left, y + 0.5f), QPointF(right, y + 0.5f)); } +boost::shared_ptr Signal::get_device() const +{ + return _dev_inst; +} + } // namespace view } // namespace pv diff --git a/DSView/pv/view/signal.h b/DSView/pv/view/signal.h index 4a18ad24..82e17e92 100644 --- a/DSView/pv/view/signal.h +++ b/DSView/pv/view/signal.h @@ -89,6 +89,8 @@ public: */ //virtual void paint_label(QPainter &p, int right, bool hover, int action); + boost::shared_ptr get_device() const; + protected: /** diff --git a/DSView/pv/view/view.cpp b/DSView/pv/view/view.cpp index f35d7a2e..7c9cd6cc 100644 --- a/DSView/pv/view/view.cpp +++ b/DSView/pv/view/view.cpp @@ -579,6 +579,12 @@ void View::signals_changed() uint8_t max_height = MaxHeightUnit; vector< boost::shared_ptr > time_traces; vector< boost::shared_ptr > fft_traces; + + if (_session.get_device()->dev_inst()->mode == LOGIC) + _viewbottom->setFixedHeight(StatusHeight); + else + _viewbottom->setFixedHeight(10); + BOOST_FOREACH(const boost::shared_ptr t, get_traces(ALL_VIEW)) { if (_trace_view_map[t->get_type()] == TIME_VIEW) time_traces.push_back(t); @@ -666,7 +672,6 @@ 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); @@ -683,7 +688,6 @@ bool View::eventFilter(QObject *object, QEvent *event) _hover_point = QPoint(-1, -1); hover_point_changed(); - } else if (type == QEvent::Leave) { _hover_point = QPoint(-1, -1); hover_point_changed(); diff --git a/DSView/pv/view/view.h b/DSView/pv/view/view.h index af6f9337..cfc5466b 100644 --- a/DSView/pv/view/view.h +++ b/DSView/pv/view/view.h @@ -84,7 +84,7 @@ public: static constexpr double MaxViewRate = 1.0; static const int MaxPixelsPerSample = 100; - static const int StatusHeight = 25; + static const int StatusHeight = 20; public: explicit View(SigSession &session, pv::toolbars::SamplingBar *sampling_bar, QWidget *parent = 0); @@ -193,7 +193,6 @@ public: void viewport_update(); - signals: void hover_point_changed(); @@ -233,6 +232,7 @@ public slots: void show_region(uint64_t start, uint64_t end); // -- calibration void update_calibration(); + void hide_calibration(); void status_clear(); private slots: @@ -256,7 +256,6 @@ private slots: // calibration for oscilloscope void show_calibration(); - void hide_calibration(); void on_measure_updated(); void splitterMoved(int pos, int index); diff --git a/DSView/pv/widgets/border.cpp b/DSView/pv/widgets/border.cpp new file mode 100644 index 00000000..e11d6d6f --- /dev/null +++ b/DSView/pv/widgets/border.cpp @@ -0,0 +1,107 @@ +/* + * This file is part of the DSView project. + * DSView is based on PulseView. + * + * Copyright (C) 2016 DreamSourceLab + * + * 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 "border.h" +#include "../mainframe.h" + +#include +#include +#include + +namespace pv { +namespace widgets { + +Border::Border(int type, QWidget *parent) : + _type(type), + QWidget(parent) +{ + setAttribute(Qt::WA_TranslucentBackground); + setMouseTracking(true); +} + +void Border::paintEvent(QPaintEvent *) +{ + QPainter painter(this); + painter.setPen(Qt::NoPen); + painter.setRenderHint(QPainter::Antialiasing, true); + QLinearGradient linearGrad(QPointF(width(), height()), QPointF(0, 0)); + linearGrad.setColorAt(0, QColor(48, 47, 47, 255)); + linearGrad.setColorAt(0.25, QColor(48, 47, 47, 255)); + linearGrad.setColorAt(0.5, QColor(48, 47, 47, 255)); + linearGrad.setColorAt(0.75, QColor(48, 47, 47, 100)); + linearGrad.setColorAt(1, QColor(48, 47, 47, 10)); + + QRadialGradient radialGrad(QPointF(0, 0), width()); + radialGrad.setColorAt(0, QColor(48, 47, 47, 255)); + radialGrad.setColorAt(0.25, QColor(48, 47, 47, 255)); + radialGrad.setColorAt(0.5, QColor(48, 47, 47, 255)); + radialGrad.setColorAt(0.75, QColor(48, 47, 47, 100)); + radialGrad.setColorAt(1, QColor(48, 47, 47, 10)); + + if (_type == pv::MainFrame::TopLeft) { + QRectF rectangle(0, 0, width()*2, height()*2); + radialGrad.setCenter(QPointF(width(), height())); + radialGrad.setFocalPoint(QPointF(width(), height())); + painter.setBrush(QBrush(radialGrad)); + painter.drawPie(rectangle, 90 * 16, 180 * 16); + } else if (_type == pv::MainFrame::TopRight) { + QRectF rectangle(-width(), 0, width()*2, height()*2); + radialGrad.setCenter(QPointF(0, height())); + radialGrad.setFocalPoint(QPointF(0, height())); + painter.setBrush(QBrush(radialGrad)); + painter.drawPie(rectangle, 0 * 16, 90 * 16); + } else if (_type == pv::MainFrame::BottomLeft) { + QRectF rectangle(0, -height(), width()*2, height()*2); + radialGrad.setCenter(QPointF(width(), 0)); + radialGrad.setFocalPoint(QPointF(width(), 0)); + painter.setBrush(QBrush(radialGrad)); + painter.drawPie(rectangle, 180 * 16, 270 * 16); + } else if (_type == pv::MainFrame::BottomRight) { + QRectF rectangle(-width(), -height(), width()*2, height()*2); + radialGrad.setCenter(QPointF(0, 0)); + radialGrad.setFocalPoint(QPointF(0, 0)); + painter.setBrush(QBrush(radialGrad)); + painter.drawPie(rectangle, 270 * 16, 360 * 16); + } else if (_type == pv::MainFrame::Top) { + linearGrad.setStart(QPointF(0, height())); + linearGrad.setFinalStop(QPointF(0, 0)); + painter.setBrush(QBrush(linearGrad)); + painter.drawRect(rect()); + } else if (_type == pv::MainFrame::Bottom) { + linearGrad.setStart(QPointF(0, 0)); + linearGrad.setFinalStop(QPointF(0, height())); + painter.setBrush(QBrush(linearGrad)); + painter.drawRect(rect()); + } else if (_type == pv::MainFrame::Left) { + linearGrad.setStart(QPointF(width(), 0)); + linearGrad.setFinalStop(QPointF(0, 0)); + painter.setBrush(QBrush(linearGrad)); + painter.drawRect(rect()); + } else if (_type == pv::MainFrame::Right) { + linearGrad.setStart(QPointF(0, 0)); + linearGrad.setFinalStop(QPointF(width(), 0)); + painter.setBrush(QBrush(linearGrad)); + painter.drawRect(rect()); + } +} + +} // namespace widgets +} // namespace pv diff --git a/DSView/pv/widgets/border.h b/DSView/pv/widgets/border.h new file mode 100644 index 00000000..970ac42f --- /dev/null +++ b/DSView/pv/widgets/border.h @@ -0,0 +1,46 @@ +/* + * This file is part of the DSView project. + * DSView is based on PulseView. + * + * Copyright (C) 2016 DreamSourceLab + * + * 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 + */ + +#ifndef DSVIEW_PV_WIDGETS_BORDER_H +#define DSVIEW_PV_WIDGETS_BORDER_H + +#include + +namespace pv { +namespace widgets { + +class Border : public QWidget +{ + Q_OBJECT +public: + explicit Border(int type, QWidget *parent = 0); + +protected: + void paintEvent(QPaintEvent *); + +private: + int _type; +}; + +} // namespace widgets +} // namespace pv + +#endif // DSVIEW_PV_WIDGETS_BORDER_H diff --git a/DSView/pv/widgets/decodergroupbox.cpp b/DSView/pv/widgets/decodergroupbox.cpp index ab028284..b7bf92e2 100644 --- a/DSView/pv/widgets/decodergroupbox.cpp +++ b/DSView/pv/widgets/decodergroupbox.cpp @@ -52,7 +52,7 @@ DecoderGroupBox::DecoderGroupBox(boost::shared_ptr &decoder_ setLayout(_layout); _layout->addWidget(new QLabel(QString("

%1

").arg(_dec->decoder()->name), this), - 0, 0); + 0, 0); _layout->setColumnStretch(0, 1); const srd_decoder *const d = _dec->decoder(); From 4f2e5c2e1058b3657708746588cf06a602714834 Mon Sep 17 00:00:00 2001 From: DreamSourceLab Date: Tue, 21 Jun 2016 21:52:22 +0800 Subject: [PATCH 24/32] Avoid UI freezing when search in protocol list viewer --- DSView/pv/data/decodermodel.h | 4 +-- DSView/pv/data/decoderstack.cpp | 17 ++++++++++- DSView/pv/data/decoderstack.h | 2 ++ DSView/pv/dock/protocoldock.cpp | 51 +++++++++++++++++++++++++++++++-- DSView/pv/dock/protocoldock.h | 10 +++++++ DSView/pv/view/decodetrace.cpp | 40 -------------------------- 6 files changed, 79 insertions(+), 45 deletions(-) diff --git a/DSView/pv/data/decodermodel.h b/DSView/pv/data/decodermodel.h index 4616e250..09032729 100644 --- a/DSView/pv/data/decodermodel.h +++ b/DSView/pv/data/decodermodel.h @@ -43,8 +43,8 @@ class DecoderModel : public QAbstractTableModel public: DecoderModel(QObject *parent = 0); - int rowCount(const QModelIndex &parent) const; - int columnCount(const QModelIndex &parent) const; + int rowCount(const QModelIndex & /*parent*/) const; + int columnCount(const QModelIndex & /*parent*/) const; QVariant data(const QModelIndex &index, int role) const; QVariant headerData(int section, Qt::Orientation orientation, int role) const; diff --git a/DSView/pv/data/decoderstack.cpp b/DSView/pv/data/decoderstack.cpp index 58827a03..81134b48 100644 --- a/DSView/pv/data/decoderstack.cpp +++ b/DSView/pv/data/decoderstack.cpp @@ -297,6 +297,21 @@ uint64_t DecoderStack::list_annotation_size() const return max_annotation_size; } +uint64_t DecoderStack::list_annotation_size(uint16_t row_index) const +{ + //lock_guard lock(_output_mutex); + //int row = 0; + for (map::const_iterator i = _rows.begin(); + i != _rows.end(); i++) { + map::const_iterator iter = _rows_lshow.find((*i).first); + if (iter != _rows_lshow.end() && (*iter).second) + if (row_index-- == 0) { + return (*i).second.get_annotation_size(); + } + } + return 0; +} + bool DecoderStack::list_annotation(pv::data::decode::Annotation &ann, uint16_t row_index, uint64_t col_index) const { @@ -387,6 +402,7 @@ void DecoderStack::begin_decode() if (!_options_changed) return; + _options_changed = false; // if (_decode_thread.joinable()) { // _decode_thread.interrupt(); // _decode_thread.join(); @@ -545,7 +561,6 @@ void DecoderStack::decode_data( } entry_cnt++; } - _options_changed = false; decode_done(); } diff --git a/DSView/pv/data/decoderstack.h b/DSView/pv/data/decoderstack.h index 8e0d882f..afaa041a 100644 --- a/DSView/pv/data/decoderstack.h +++ b/DSView/pv/data/decoderstack.h @@ -107,6 +107,8 @@ public: bool has_annotations(const decode::Row &row) const; uint64_t list_annotation_size() const; + uint64_t list_annotation_size(uint16_t row_index) const; + bool list_annotation(decode::Annotation &ann, uint16_t row_index, uint64_t col_index) const; diff --git a/DSView/pv/dock/protocoldock.cpp b/DSView/pv/dock/protocoldock.cpp index eaabcb8d..0db2ebe2 100644 --- a/DSView/pv/dock/protocoldock.cpp +++ b/DSView/pv/dock/protocoldock.cpp @@ -40,6 +40,9 @@ #include #include #include +#include +#include +#include #include #include @@ -50,7 +53,9 @@ namespace dock { ProtocolDock::ProtocolDock(QWidget *parent, SigSession &session) : QScrollArea(parent), _session(session), - _cur_search_index(-1) + _cur_search_index(-1), + _search_edited(false), + _searching(false) { _up_widget = new QWidget(this); @@ -199,7 +204,7 @@ ProtocolDock::ProtocolDock(QWidget *parent, SigSession &session) : connect(_table_view, SIGNAL(clicked(QModelIndex)), this, SLOT(item_clicked(QModelIndex))); connect(_table_view->horizontalHeader(), SIGNAL(sectionResized(int,int,int)), this, SLOT(column_resize(int, int, int))); //connect(_table_view->verticalScrollBar(), SIGNAL(sliderMoved()), this, SLOT(sliderMoved())); - connect(_search_edit, SIGNAL(editingFinished()), this, SLOT(search_done())); + connect(_search_edit, SIGNAL(editingFinished()), this, SLOT(search_changed())); } ProtocolDock::~ProtocolDock() @@ -543,6 +548,7 @@ void ProtocolDock::export_table_view() void ProtocolDock::search_pre() { + search_update(); // now the proxy only contains rows that match the name // let's take the pre one and map it to the original model if (_model_proxy.rowCount() == 0) { @@ -598,6 +604,7 @@ void ProtocolDock::search_pre() void ProtocolDock::search_nxt() { + search_update(); // now the proxy only contains rows that match the name // let's take the pre one and map it to the original model if (_model_proxy.rowCount() == 0) { @@ -663,6 +670,46 @@ void ProtocolDock::search_done() _matchs_label->setText(QString::number(_model_proxy.rowCount())); } +void ProtocolDock::search_changed() +{ + _search_edited = true; + _matchs_label->setText("..."); +} + +void ProtocolDock::search_update() +{ + if (!_search_edited) + return; + + pv::data::DecoderModel *decoder_model = _session.get_decoder_model(); + boost::shared_ptr decoder_stack = decoder_model->getDecoderStack(); + if (!decoder_stack) + return; + + if (decoder_stack->list_annotation_size(_model_proxy.filterKeyColumn()) > ProgressRows) { + QFuture future; + future = QtConcurrent::run([&]{ + search_done(); + }); + Qt::WindowFlags flags = Qt::CustomizeWindowHint; + QProgressDialog dlg(tr("Searching..."), + tr("Cancel"),0,0,this,flags); + dlg.setWindowModality(Qt::WindowModal); + dlg.setWindowFlags(Qt::Dialog | Qt::FramelessWindowHint); + dlg.setCancelButton(NULL); + + QFutureWatcher watcher; + watcher.setFuture(future); + connect(&watcher,SIGNAL(finished()),&dlg,SLOT(cancel())); + + dlg.exec(); + } else { + search_done(); + } + _search_edited = false; + //search_done(); +} + } // namespace dock } // namespace pv diff --git a/DSView/pv/dock/protocoldock.h b/DSView/pv/dock/protocoldock.h index bcf4bffd..1c08c875 100644 --- a/DSView/pv/dock/protocoldock.h +++ b/DSView/pv/dock/protocoldock.h @@ -38,6 +38,7 @@ #include #include +#include #include @@ -57,6 +58,9 @@ class ProtocolDock : public QScrollArea { Q_OBJECT +public: + static const uint64_t ProgressRows = 100000; + public: ProtocolDock(QWidget *parent, SigSession &session); ~ProtocolDock(); @@ -80,6 +84,8 @@ private slots: void search_pre(); void search_nxt(); void search_done(); + void search_changed(); + void search_update(); private: static int decoder_name_cmp(const void *a, const void *b); @@ -113,6 +119,10 @@ private: QPushButton *_dn_set_button; QPushButton *_dn_save_button; + + mutable boost::mutex _search_mutex; + bool _search_edited; + bool _searching; }; } // namespace dock diff --git a/DSView/pv/view/decodetrace.cpp b/DSView/pv/view/decodetrace.cpp index 1887de34..622c5526 100644 --- a/DSView/pv/view/decodetrace.cpp +++ b/DSView/pv/view/decodetrace.cpp @@ -336,46 +336,6 @@ void DecodeTrace::paint_fore(QPainter &p, int left, int right) using namespace pv::data::decode; (void)right; - -// const int row_height = _view->get_signalHeight(); - -// for (size_t i = 0; i < _cur_row_headings.size(); i++) -// { -// const int y = (i + 0.5) * row_height + get_y() - _totalHeight * 0.5; - -// p.setPen(QPen(Qt::NoPen)); -// p.setBrush(QApplication::palette().brush(QPalette::WindowText)); - -// if (i != 0) -// { -// const QPointF points[] = { -// QPointF(left, y - ArrowSize), -// QPointF(left + ArrowSize, y), -// QPointF(left, y + ArrowSize) -// }; -// p.drawPolygon(points, countof(points)); -// } - -// const QRect r(left + ArrowSize * 2, y - row_height / 2, -// right - left, row_height); -// const QString h(_cur_row_headings[i]); -// const int f = Qt::AlignLeft | Qt::AlignBottom | -// Qt::TextDontClip; - -// // Draw the outline -// QFont font=p.font(); -// font.setPointSize(DefaultFontSize); -// p.setFont(font); -//// p.setPen(QApplication::palette().color(QPalette::Base)); -//// for (int dx = -1; dx <= 1; dx++) -//// for (int dy = -1; dy <= 1; dy++) -//// if (dx != 0 && dy != 0) -//// p.drawText(r.translated(dx, dy), f, h); - -// // Draw the text -// p.setPen(DARK_FORE); -// p.drawText(r, f, h); -// } } bool DecodeTrace::create_popup() From d86c9558cbe25f2de7569d0f9d44fe538a1e4f38 Mon Sep 17 00:00:00 2001 From: DreamSourceLab Date: Tue, 21 Jun 2016 23:26:46 +0800 Subject: [PATCH 25/32] Add file path remember feature --- DSView/pv/dialogs/protocolexp.cpp | 7 +++++- DSView/pv/mainframe.cpp | 37 ++++++++++++++++++++++++++----- DSView/pv/mainframe.h | 9 +++++++- DSView/pv/mainwindow.cpp | 19 +++++++++++----- DSView/pv/toolbars/filebar.cpp | 37 ++++++++++++++++++++++++------- 5 files changed, 88 insertions(+), 21 deletions(-) diff --git a/DSView/pv/dialogs/protocolexp.cpp b/DSView/pv/dialogs/protocolexp.cpp index b4a6f988..d0907af8 100644 --- a/DSView/pv/dialogs/protocolexp.cpp +++ b/DSView/pv/dialogs/protocolexp.cpp @@ -114,9 +114,11 @@ void ProtocolExp::accept() if(i < supportedFormats.count() - 1) filter.append(";;"); } + const QString DIR_KEY("ProtocolExportPath"); + QSettings settings; QString default_filter = _format_combobox->currentText(); QString file_name = QFileDialog::getSaveFileName( - this, tr("Export Data"), "",filter,&default_filter); + this, tr("Export Data"), settings.value(DIR_KEY).toString(),filter,&default_filter); if (!file_name.isEmpty()) { QFileInfo f(file_name); QStringList list = default_filter.split('.').last().split(')'); @@ -124,6 +126,9 @@ void ProtocolExp::accept() if(f.suffix().compare(ext)) file_name+=tr(".")+ext; + QDir CurrentDir; + settings.setValue(DIR_KEY, CurrentDir.absoluteFilePath(file_name)); + QFile file(file_name); file.open(QIODevice::WriteOnly | QIODevice::Text); QTextStream out(&file); diff --git a/DSView/pv/mainframe.cpp b/DSView/pv/mainframe.cpp index 6049f41a..74f822a5 100644 --- a/DSView/pv/mainframe.cpp +++ b/DSView/pv/mainframe.cpp @@ -33,6 +33,8 @@ #include #include #include +#include +#include #include @@ -109,12 +111,14 @@ MainFrame::MainFrame(DeviceManager &device_manager, _layout->addWidget(_bottom_right, 2, 2); connect(&_timer, SIGNAL(timeout()), this, SLOT(unfreezing())); + readSettings(); } -bool MainFrame::close() +void MainFrame::closeEvent(QCloseEvent *event) { _mainWindow->session_save(); - return QFrame::close(); + writeSettings(); + event->accept(); } void MainFrame::unfreezing() @@ -200,7 +204,7 @@ bool MainFrame::eventFilter(QObject *object, QEvent *event) } else if(mouse_event->buttons().testFlag(Qt::LeftButton)) { if (_moving) { this->move(mouse_event->globalPos() - _lastMousePosition); - } else if (!_freezing){ + } else if (!_freezing) { switch (_startPos) { case TopLeft: newWidth = std::max(_dragStartGeometry.right() - mouse_event->globalX(), minimumWidth()); @@ -266,7 +270,7 @@ bool MainFrame::eventFilter(QObject *object, QEvent *event) break; } _freezing = true; - } + } return true; } } else if (type == QEvent::MouseButtonPress) { @@ -275,7 +279,7 @@ bool MainFrame::eventFilter(QObject *object, QEvent *event) _startPos == None) { _moving = true; _lastMousePosition = mouse_event->pos() + - QPoint(Margin, Margin) + + //QPoint(Margin, Margin) + QPoint(geometry().left() - frameGeometry().left(), frameGeometry().right() - geometry().right()); } if (_startPos != None) @@ -304,4 +308,27 @@ bool MainFrame::eventFilter(QObject *object, QEvent *event) return QObject::eventFilter(object, event); } +void MainFrame::writeSettings() +{ + QSettings settings; + + settings.beginGroup("MainFrame"); + settings.setValue("size", size()); + settings.setValue("pos", pos() + + QPoint(geometry().left() - frameGeometry().left(), frameGeometry().right() - geometry().right())); + settings.endGroup(); +} + +void MainFrame::readSettings() +{ + QSettings settings; + QDesktopWidget* desktopWidget = QApplication::desktop(); + QRect deskRect = desktopWidget->availableGeometry(); + + settings.beginGroup("MainFrame"); + resize(settings.value("size", QSize(minWidth, minHeight)).toSize()); + move(settings.value("pos", QPoint((deskRect.width() - minWidth)/2, (deskRect.height() - minHeight)/2)).toPoint()); + settings.endGroup(); +} + } // namespace pv diff --git a/DSView/pv/mainframe.h b/DSView/pv/mainframe.h index eafc317c..50432639 100644 --- a/DSView/pv/mainframe.h +++ b/DSView/pv/mainframe.h @@ -41,6 +41,9 @@ class TitleBar; class MainFrame : public QFrame { Q_OBJECT +public: + static const int minWidth = 800; + static const int minHeight = 680; public: static const int Margin = 8; @@ -64,17 +67,21 @@ public: void showMaxRestore(); protected: + void closeEvent(QCloseEvent *event); bool eventFilter(QObject *object, QEvent *event); public slots: void unfreezing(); - bool close(); void showNormal(); void showMaximized(); + private: void hide_border(); void show_border(); + void writeSettings(); + void readSettings(); + private: toolbars::TitleBar *_titleBar; MainWindow *_mainWindow; diff --git a/DSView/pv/mainwindow.cpp b/DSView/pv/mainwindow.cpp index ca9825e4..4bf1f35b 100644 --- a/DSView/pv/mainwindow.cpp +++ b/DSView/pv/mainwindow.cpp @@ -571,35 +571,42 @@ void MainWindow::on_search(bool visible) void MainWindow::on_screenShot() { + const QString DIR_KEY("ScreenShotPath"); + QSettings settings; QPixmap pixmap; QDesktopWidget *desktop = QApplication::desktop(); pixmap = QPixmap::grabWindow(desktop->winId(), pos().x(), pos().y(), frameGeometry().width(), frameGeometry().height()); QString format = "png"; - QString initialPath = QDir::currentPath()+ - tr("/untitled.") + format; QString fileName = QFileDialog::getSaveFileName(this, - tr("Save As"),initialPath, + tr("Save As"),settings.value(DIR_KEY).toString(), tr("%1 Files (*.%2);;All Files (*)") .arg(format.toUpper()).arg(format)); - if (!fileName.isEmpty()) + if (!fileName.isEmpty()) { + QDir CurrentDir; + settings.setValue(DIR_KEY, CurrentDir.absoluteFilePath(fileName)); pixmap.save(fileName, format.toLatin1()); + } } void MainWindow::on_save() { using pv::dialogs::StoreProgress; + const QString DIR_KEY("SavePath"); + QSettings settings; + // Stop any currently running capture session _session.stop_capture(); // Show the dialog const QString file_name = QFileDialog::getSaveFileName( - this, tr("Save File"), "", tr("DSView Data (*.dsl)")); + this, tr("Save File"), settings.value(DIR_KEY).toString(), tr("DSView Data (*.dsl)")); if (file_name.isEmpty()) return; - + QDir CurrentDir; + settings.setValue(DIR_KEY, CurrentDir.absoluteFilePath(file_name)); StoreProgress *dlg = new StoreProgress(file_name, _session, this); dlg->run(); } diff --git a/DSView/pv/toolbars/filebar.cpp b/DSView/pv/toolbars/filebar.cpp index 9f10daa6..e76c354c 100644 --- a/DSView/pv/toolbars/filebar.cpp +++ b/DSView/pv/toolbars/filebar.cpp @@ -123,12 +123,17 @@ FileBar::FileBar(SigSession &session, QWidget *parent) : void FileBar::on_actionOpen_triggered() { + const QString DIR_KEY("OpenPath"); + QSettings settings; // Show the dialog const QString file_name = QFileDialog::getOpenFileName( - this, tr("Open File"), "", tr( + this, tr("Open File"), settings.value(DIR_KEY).toString(), tr( "DSView Data (*.dsl);;All Files (*.*)")); - if (!file_name.isEmpty()) + if (!file_name.isEmpty()) { + QDir CurrentDir; + settings.setValue(DIR_KEY, CurrentDir.absoluteFilePath(file_name)); load_file(file_name); + } } void FileBar::session_error( @@ -151,6 +156,8 @@ void FileBar::show_session_error( } void FileBar::on_actionExport_triggered(){ + const QString DIR_KEY("ExportPath"); + QSettings settings; int unit_size; uint64_t length; const void* buf = _session.get_buf(unit_size, length); @@ -170,13 +177,15 @@ void FileBar::on_actionExport_triggered(){ filter.append(";;"); } QString file_name = QFileDialog::getSaveFileName( - this, tr("Export Data"), "",filter,&filter); + this, tr("Export Data"), settings.value(DIR_KEY).toString(),filter,&filter); if (!file_name.isEmpty()) { QFileInfo f(file_name); QStringList list = filter.split('.').last().split(')'); QString ext = list.first(); if(f.suffix().compare(ext)) file_name+=tr(".")+ext; + QDir CurrentDir; + settings.setValue(DIR_KEY, CurrentDir.absoluteFilePath(file_name)); _session.export_file(file_name, this, ext); } } @@ -184,6 +193,8 @@ void FileBar::on_actionExport_triggered(){ void FileBar::on_actionSave_triggered() { + const QString DIR_KEY("SavePath"); + QSettings settings; //save(); int unit_size; uint64_t length; @@ -197,13 +208,15 @@ void FileBar::on_actionSave_triggered() msg.exec(); } else { QString file_name = QFileDialog::getSaveFileName( - this, tr("Save File"), "", + this, tr("Save File"), settings.value(DIR_KEY).toString(), tr("DSView Data (*.dsl)")); if (!file_name.isEmpty()) { QFileInfo f(file_name); if(f.suffix().compare("dsl")) file_name.append(tr(".dsl")); + QDir CurrentDir; + settings.setValue(DIR_KEY, CurrentDir.absoluteFilePath(file_name)); _session.save_file(file_name, this, _session.get_device()->dev_inst()->mode); } } @@ -212,12 +225,17 @@ void FileBar::on_actionSave_triggered() void FileBar::on_actionLoad_triggered() { + const QString DIR_KEY("SessionLoadPath"); + QSettings settings; // Show the dialog const QString file_name = QFileDialog::getOpenFileName( - this, tr("Open Session"), "", tr( + this, tr("Open Session"), settings.value(DIR_KEY).toString(), tr( "DSView Session (*.dsc)")); - if (!file_name.isEmpty()) + if (!file_name.isEmpty()) { + QDir CurrentDir; + settings.setValue(DIR_KEY, CurrentDir.absoluteFilePath(file_name)); load_session(file_name); + } } void FileBar::on_actionDefault_triggered() @@ -242,14 +260,17 @@ void FileBar::on_actionDefault_triggered() void FileBar::on_actionStore_triggered() { - QString default_name = _session.get_device()->name(); + const QString DIR_KEY("SessionStorePath"); + QSettings settings; QString file_name = QFileDialog::getSaveFileName( - this, tr("Save Session"), default_name, + this, tr("Save Session"), settings.value(DIR_KEY).toString(), tr("DSView Session (*.dsc)")); if (!file_name.isEmpty()) { QFileInfo f(file_name); if(f.suffix().compare("dsc")) file_name.append(tr(".dsc")); + QDir CurrentDir; + settings.setValue(DIR_KEY, CurrentDir.absoluteFilePath(file_name)); store_session(file_name); } } From 5bd97eb8c24a488a9dba7858594d75fe14810b7c Mon Sep 17 00:00:00 2001 From: DreamSourceLab Date: Wed, 22 Jun 2016 15:51:08 +0800 Subject: [PATCH 26/32] Add error display for decoder trace; Adjust comboBox width on contents --- DSView/pv/data/decoderstack.cpp | 21 +++++++++------- DSView/pv/dock/measuredock.cpp | 6 +++++ DSView/pv/dock/protocoldock.cpp | 40 +++++++++++++++++++----------- DSView/pv/dock/protocoldock.h | 7 +++++- DSView/pv/toolbars/samplingbar.cpp | 14 +++++++++++ DSView/pv/toolbars/samplingbar.h | 1 + DSView/pv/view/decodetrace.cpp | 39 +++++++++++++---------------- DSView/pv/view/decodetrace.h | 2 +- 8 files changed, 83 insertions(+), 47 deletions(-) diff --git a/DSView/pv/data/decoderstack.cpp b/DSView/pv/data/decoderstack.cpp index 81134b48..d463ecba 100644 --- a/DSView/pv/data/decoderstack.cpp +++ b/DSView/pv/data/decoderstack.cpp @@ -499,6 +499,7 @@ void DecoderStack::decode_data( uint64_t entry_cnt = 0; uint8_t chunk_type = 0; uint64_t i = decode_start; + char *error = NULL; while(!boost::this_thread::interruption_requested() && i < decode_end && !_no_memory) { @@ -509,8 +510,8 @@ void DecoderStack::decode_data( chunk = _snapshot->get_samples(i, chunk_end); if (srd_session_send(session, chunk_type, i, chunk_end, chunk, - (chunk_end - i) * unit_size, unit_size) != SRD_OK) { - _error_message = tr("Decoder reported an error"); + (chunk_end - i) * unit_size, unit_size, &error) != SRD_OK) { + _error_message = QString::fromLocal8Bit(error); break; } @@ -561,6 +562,8 @@ void DecoderStack::decode_data( } entry_cnt++; } + if (error) + g_free(error); decode_done(); } @@ -617,15 +620,15 @@ void DecoderStack::decode_proc() srd_pd_output_callback_add(session, SRD_OUTPUT_ANN, DecoderStack::annotation_callback, this); - srd_session_start(session); - -// do { -// decode_data(*sample_count, unit_size, session); -// } while(_error_message.isEmpty() && (sample_count = wait_for_data())); - //decode_data(*sample_count, unit_size, session); - decode_data(decode_start, decode_end, unit_size, session); + char *error = NULL; + if (srd_session_start(session, &error) == SRD_OK) + decode_data(decode_start, decode_end, unit_size, session); + else + _error_message = QString::fromLocal8Bit(error); // Destroy the session + if (error) + g_free(error); srd_session_destroy(session); _decode_state = Stopped; diff --git a/DSView/pv/dock/measuredock.cpp b/DSView/pv/dock/measuredock.cpp index 1bac919a..9706eb2d 100644 --- a/DSView/pv/dock/measuredock.cpp +++ b/DSView/pv/dock/measuredock.cpp @@ -92,6 +92,9 @@ MeasureDock::MeasureDock(QWidget *parent, View &view, SigSession &session) : _t1_last_index = 0; _t2_last_index = 0; _t3_last_index = 0; + _t1_comboBox->setSizeAdjustPolicy(QComboBox::AdjustToContents); + _t2_comboBox->setSizeAdjustPolicy(QComboBox::AdjustToContents); + _t3_comboBox->setSizeAdjustPolicy(QComboBox::AdjustToContents); _cursor_layout = new QGridLayout(_widget); _cursor_layout->addWidget(new QLabel(tr("T1: "), _widget), 0, 0); @@ -203,6 +206,9 @@ void MeasureDock::cursor_update() index++; } + _t1_comboBox->setMinimumWidth(_t1_comboBox->sizeHint().width()+15); + _t2_comboBox->setMinimumWidth(_t2_comboBox->sizeHint().width()+15); + _t3_comboBox->setMinimumWidth(_t3_comboBox->sizeHint().width()+15); if (_t1_last_index < _t1_comboBox->count()) _t1_comboBox->setCurrentIndex(_t1_last_index); diff --git a/DSView/pv/dock/protocoldock.cpp b/DSView/pv/dock/protocoldock.cpp index 0db2ebe2..179d35d7 100644 --- a/DSView/pv/dock/protocoldock.cpp +++ b/DSView/pv/dock/protocoldock.cpp @@ -43,9 +43,11 @@ #include #include #include +#include #include #include +#include namespace pv { namespace dock { @@ -154,21 +156,19 @@ ProtocolDock::ProtocolDock(QWidget *parent, SigSession &session) : search_button->setFixedWidth(search_button->height()); search_button->setDisabled(true); _search_edit = new QLineEdit(_dn_widget); + _search_edit->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); _search_edit->setPlaceholderText(tr("search")); QHBoxLayout *search_layout = new QHBoxLayout(); search_layout->addWidget(search_button); - search_layout->addStretch(); + search_layout->addStretch(1); search_layout->setContentsMargins(0, 0, 0, 0); _search_edit->setLayout(search_layout); _search_edit->setTextMargins(search_button->width(), 0, 0, 0); - QSizePolicy sp = _search_edit->sizePolicy(); - sp.setHorizontalStretch(1); - _search_edit->setSizePolicy(sp); - QHBoxLayout *dn_search_layout = new QHBoxLayout(); - dn_search_layout->addWidget(_pre_button, 0, Qt::AlignLeft); - dn_search_layout->addWidget(_search_edit, 0, Qt::AlignLeft); - dn_search_layout->addWidget(_nxt_button, 0, Qt::AlignRight); + _dn_search_layout = new QHBoxLayout(); + _dn_search_layout->addWidget(_pre_button, 0, Qt::AlignLeft); + _dn_search_layout->addWidget(_search_edit, 1, Qt::AlignLeft); + _dn_search_layout->addWidget(_nxt_button, 0, Qt::AlignRight); _matchs_label = new QLabel(_dn_widget); QHBoxLayout *dn_match_layout = new QHBoxLayout(); @@ -176,13 +176,13 @@ ProtocolDock::ProtocolDock(QWidget *parent, SigSession &session) : dn_match_layout->addWidget(_matchs_label, 0, Qt::AlignLeft); dn_match_layout->addStretch(1); - QVBoxLayout *dn_layout = new QVBoxLayout(); - dn_layout->addLayout(dn_title_layout); - dn_layout->addLayout(dn_search_layout); - dn_layout->addLayout(dn_match_layout); - dn_layout->addWidget(_table_view); + _dn_layout = new QVBoxLayout(); + _dn_layout->addLayout(dn_title_layout); + _dn_layout->addLayout(_dn_search_layout); + _dn_layout->addLayout(dn_match_layout); + _dn_layout->addWidget(_table_view); - _dn_widget->setLayout(dn_layout); + _dn_widget->setLayout(_dn_layout); _dn_widget->setMinimumHeight(350); _split_widget = new QSplitter(this); @@ -219,6 +219,18 @@ void ProtocolDock::paintEvent(QPaintEvent *) // style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); } +void ProtocolDock::resizeEvent(QResizeEvent *event) +{ + int width = this->visibleRegion().boundingRect().width(); + width = width - _dn_layout->margin() * 2 - + _dn_search_layout->margin() * 2 - + _dn_search_layout->spacing() * 2 - + _pre_button->width()-_nxt_button->width(); + width = std::max(width, 0); + _search_edit->setMinimumWidth(width); + QScrollArea::resizeEvent(event); +} + int ProtocolDock::decoder_name_cmp(const void *a, const void *b) { return strcmp(((const srd_decoder*)a)->name, diff --git a/DSView/pv/dock/protocoldock.h b/DSView/pv/dock/protocoldock.h index 1c08c875..9a1616fd 100644 --- a/DSView/pv/dock/protocoldock.h +++ b/DSView/pv/dock/protocoldock.h @@ -64,10 +64,13 @@ public: public: ProtocolDock(QWidget *parent, SigSession &session); ~ProtocolDock(); - void paintEvent(QPaintEvent *); void del_all_protocol(); +protected: + void paintEvent(QPaintEvent *); + void resizeEvent(QResizeEvent *); + signals: void protocol_updated(); @@ -104,6 +107,8 @@ private: QPushButton *_pre_button; QPushButton *_nxt_button; QLineEdit *_search_edit; + QHBoxLayout *_dn_search_layout; + QVBoxLayout *_dn_layout; QLabel *_matchs_label; QPushButton *_add_button; diff --git a/DSView/pv/toolbars/samplingbar.cpp b/DSView/pv/toolbars/samplingbar.cpp index b2ca6c0c..94eb9801 100644 --- a/DSView/pv/toolbars/samplingbar.cpp +++ b/DSView/pv/toolbars/samplingbar.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #include "samplingbar.h" @@ -114,7 +115,11 @@ SamplingBar::SamplingBar(SigSession &session, QWidget *parent) : _run_stop_button.setIcon(_icon_start); _instant_button.setIcon(_icon_instant); + _device_selector.setSizeAdjustPolicy(QComboBox::AdjustToContents); + _sample_rate.setSizeAdjustPolicy(QComboBox::AdjustToContents); _sample_count.setSizeAdjustPolicy(QComboBox::AdjustToContents); + _device_selector.setMaximumWidth(ComboBoxMaxWidth); + set_sampling(false); connect(&_sample_count, SIGNAL(currentIndexChanged(int)), this, SLOT(on_samplecount_sel(int))); @@ -163,6 +168,9 @@ void SamplingBar::set_device_list( _device_selector.addItem(title, qVariantFromValue((void*)id)); } + int width = _device_selector.sizeHint().width(); + _device_selector.setFixedWidth(min(width+15, _device_selector.maximumWidth())); + _device_selector.view()->setMinimumWidth(width+30); // The selected device should have been in the list assert(selected_index != -1); @@ -407,6 +415,10 @@ void SamplingBar::update_sample_rate_selector() _sample_rate.show(); g_variant_unref(gvar_list); } + + _sample_rate.setMinimumWidth(_sample_rate.sizeHint().width()+15); + _sample_rate.view()->setMinimumWidth(_sample_rate.sizeHint().width()+30); + _updating_sample_rate = false; g_variant_unref(gvar_dict); @@ -576,6 +588,8 @@ void SamplingBar::update_sample_count_selector() _sample_count.show(); g_variant_unref(gvar_list); } + _sample_count.setMinimumWidth(_sample_count.sizeHint().width()+15); + _sample_count.view()->setMinimumWidth(_sample_count.sizeHint().width()+30); _updating_sample_count = false; g_variant_unref(gvar_dict); diff --git a/DSView/pv/toolbars/samplingbar.h b/DSView/pv/toolbars/samplingbar.h index 7c9fa4bb..14e9aa94 100644 --- a/DSView/pv/toolbars/samplingbar.h +++ b/DSView/pv/toolbars/samplingbar.h @@ -63,6 +63,7 @@ class SamplingBar : public QToolBar private: static const uint64_t RecordLengths[19]; static const uint64_t DefaultRecordLength; + static const int ComboBoxMaxWidth = 200; public: SamplingBar(SigSession &session, QWidget *parent); diff --git a/DSView/pv/view/decodetrace.cpp b/DSView/pv/view/decodetrace.cpp index 622c5526..ab411632 100644 --- a/DSView/pv/view/decodetrace.cpp +++ b/DSView/pv/view/decodetrace.cpp @@ -240,6 +240,14 @@ void DecodeTrace::paint_mid(QPainter &p, int left, int right) { using namespace pv::data::decode; + assert(_decoder_stack); + const QString err = _decoder_stack->error_message(); + if (!err.isEmpty()) + { + draw_error(p, err, left, right); + return; + } + const double scale = _view->scale(); assert(scale > 0); @@ -267,14 +275,6 @@ void DecodeTrace::paint_mid(QPainter &p, int left, int right) const int annotation_height = _view->get_signalHeight(); - assert(_decoder_stack); - const QString err = _decoder_stack->error_message(); - if (!err.isEmpty()) - { - draw_error(p, err, left, right); - return; - } - // Iterate through the rows assert(_view); int y = get_y() - (_totalHeight - annotation_height)*0.5; @@ -610,24 +610,19 @@ void DecodeTrace::draw_error(QPainter &p, const QString &message, int left, int right) { const int y = get_y(); + const int h = get_totalHeight(); - p.setPen(ErrorBgColour.darker()); - p.setBrush(ErrorBgColour); - - const QRectF bounding_rect = - QRectF(left, INT_MIN / 2 + y, right - left, INT_MAX); - const QRectF text_rect = p.boundingRect(bounding_rect, - Qt::AlignCenter, message); - const float r = text_rect.height() / 4; - - p.drawRoundedRect(text_rect.adjusted(-r, -r, r, r), r, r, - Qt::AbsoluteSize); - - p.setPen(get_text_colour()); + const QRectF text_rect(left, y - h/2 + 0.5, right - left, h); + const QRectF bounding_rect = p.boundingRect(text_rect, + Qt::AlignCenter, message); + p.setPen(Qt::red); QFont font=p.font(); font.setPointSize(DefaultFontSize); p.setFont(font); - p.drawText(text_rect, message); + if (bounding_rect.width() < text_rect.width()) + p.drawText(text_rect, Qt::AlignCenter, tr("Error: ")+message); + else + p.drawText(text_rect, Qt::AlignCenter, tr("Error: ...")); } void DecodeTrace::draw_unshown_row(QPainter &p, int y, int h, int left, diff --git a/DSView/pv/view/decodetrace.h b/DSView/pv/view/decodetrace.h index 9ee89d84..bf93e440 100644 --- a/DSView/pv/view/decodetrace.h +++ b/DSView/pv/view/decodetrace.h @@ -89,7 +89,7 @@ private: static const QColor Colours[16]; static const QColor OutlineColours[16]; - static const int DefaultFontSize = 8; + static const int DefaultFontSize = 10; static const int ControlRectWidth = 5; static const int MaxAnnType = 100; From 4cc02c8d780e17527cd05e0ba7aa45288e49bd8a Mon Sep 17 00:00:00 2001 From: DreamSourceLab Date: Sat, 25 Jun 2016 09:58:07 +0800 Subject: [PATCH 27/32] Fix trigger issue @ DSO mode --- DSView/darkstyle/style.qss | 39 +++++- DSView/pv/data/mathstack.cpp | 2 +- DSView/pv/dock/dsotriggerdock.cpp | 53 +++++++- DSView/pv/dock/dsotriggerdock.h | 5 +- DSView/pv/mainframe.cpp | 54 ++++++-- DSView/pv/mainframe.h | 4 + DSView/pv/mainwindow.cpp | 28 ++-- DSView/pv/sigsession.cpp | 43 ++++++- DSView/pv/sigsession.h | 10 +- DSView/pv/toolbars/filebar.cpp | 7 +- DSView/pv/toolbars/filebar.h | 2 + DSView/pv/toolbars/titlebar.cpp | 2 +- DSView/pv/toolbars/titlebar.h | 1 - DSView/pv/view/dsosignal.cpp | 185 ++++++++++++--------------- DSView/pv/view/dsosignal.h | 38 +++--- DSView/pv/view/header.cpp | 4 +- DSView/pv/view/mathtrace.cpp | 10 +- DSView/pv/view/mathtrace.h | 4 +- DSView/pv/view/timemarker.cpp | 6 +- DSView/pv/view/trace.cpp | 12 +- DSView/pv/view/trace.h | 4 +- DSView/pv/view/view.cpp | 52 ++++++-- DSView/pv/view/view.h | 9 +- DSView/pv/view/viewport.cpp | 30 ++++- DSView/pv/view/viewport.h | 7 + libsigrok4DSL/hardware/DSL/dscope.c | 37 +++++- libsigrok4DSL/hardware/DSL/dsl.h | 2 + libsigrok4DSL/hardware/DSL/dslogic.c | 142 +++++++++++++------- libsigrok4DSL/hardware/demo/demo.c | 9 +- libsigrok4DSL/hwdriver.c | 2 + libsigrok4DSL/libsigrok.h | 6 + 31 files changed, 574 insertions(+), 235 deletions(-) diff --git a/DSView/darkstyle/style.qss b/DSView/darkstyle/style.qss index ddf6b5e8..af69a9eb 100755 --- a/DSView/darkstyle/style.qss +++ b/DSView/darkstyle/style.qss @@ -958,11 +958,28 @@ QSlider::groove:horizontal { border-radius: 2px; } +QSlider::groove:horizontal:disabled { + border: 1px solid #3A3939; + height: 8px; + background: #282727; + margin: 2px 0; + border-radius: 2px; +} + QSlider::handle:horizontal { background: QLinearGradient( x1: 0, y1: 0, x2: 0, y2: 1, stop: 0.0 silver, stop: 0.2 #a8a8a8, stop: 1 #727272); border: 1px solid #3A3939; - width: 14px; + width: 10px; + height: 14px; + margin: -4px 0; + border-radius: 2px; +} + +QSlider::handle:horizontal:disabled { + background: #4A4949; + border: 1px solid #3A3939; + width: 10px; height: 14px; margin: -4px 0; border-radius: 2px; @@ -976,15 +993,33 @@ QSlider::groove:vertical { border-radius: 2px; } +QSlider::groove:vertical:disabled { + border: 1px solid #3A3939; + height: 8px; + background: #403F3F; + margin: 2px 0; + border-radius: 2px; +} + QSlider::handle:vertical { background: QLinearGradient( x1: 0, y1: 0, x2: 0, y2: 1, stop: 0.0 silver, stop: 0.2 #a8a8a8, stop: 1 #727272); border: 1px solid #3A3939; width: 14px; - height: 14px; + height: 10px; margin: 0 -4px; border-radius: 2px; } + +QSlider::handle:vertical:disabled { + background: #4A4949; + border: 1px solid #3A3939; + width: 14px; + height: 10px; + margin: 0 -4px; + border-radius: 2px; +} + QToolButton#MaximizeButton { background-color: transparent; border-left: 1px solid QLinearGradient( x1: 0, y1: 0, x2: 0, y2: 1, diff --git a/DSView/pv/data/mathstack.cpp b/DSView/pv/data/mathstack.cpp index 69f33e2c..f15745ef 100644 --- a/DSView/pv/data/mathstack.cpp +++ b/DSView/pv/data/mathstack.cpp @@ -200,7 +200,7 @@ void MathStack::calc_fft() _samplerate = 1.0; // prepare _xn data - const double offset = dsoSig->get_zeroValue(); + const double offset = dsoSig->get_zero_value(); const double vscale = dsoSig->get_vDialValue() * dsoSig->get_factor() * DS_CONF_DSO_VDIVS / (1000*255.0); const uint16_t step = _snapshot->get_channel_num() * _sample_interval; const uint8_t *const samples = _snapshot->get_samples(0, _sample_num*_sample_interval-1, _index); diff --git a/DSView/pv/dock/dsotriggerdock.cpp b/DSView/pv/dock/dsotriggerdock.cpp index 38c0542d..d46cfa5c 100644 --- a/DSView/pv/dock/dsotriggerdock.cpp +++ b/DSView/pv/dock/dsotriggerdock.cpp @@ -56,7 +56,7 @@ DsoTriggerDock::DsoTriggerDock(QWidget *parent, SigSession &session) : connect(position_spinBox, SIGNAL(valueChanged(int)), position_slider, SLOT(setValue(int))); connect(position_slider, SIGNAL(valueChanged(int)), this, SLOT(pos_changed(int))); - QLabel *holdoff_label = new QLabel(tr("Trigger Hold Off Time: "), _widget); + QLabel *holdoff_label = new QLabel(tr("Hold Off Time: "), _widget); holdoff_comboBox = new QComboBox(_widget); holdoff_comboBox->addItem(tr("uS"), qVariantFromValue(1000)); holdoff_comboBox->addItem(tr("mS"), qVariantFromValue(1000000)); @@ -71,6 +71,11 @@ DsoTriggerDock::DsoTriggerDock(QWidget *parent, SigSession &session) : connect(holdoff_slider, SIGNAL(valueChanged(int)), this, SLOT(hold_changed(int))); connect(holdoff_comboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(hold_changed(int))); + QLabel *margin_label = new QLabel(tr("Noise Sensitivity: "), _widget); + margin_slider = new QSlider(Qt::Horizontal, _widget); + margin_slider->setRange(0, 15); + connect(margin_slider, SIGNAL(valueChanged(int)), this, SLOT(margin_changed(int))); + QLabel *tSource_labe = new QLabel(tr("Trigger Sources: "), _widget); QRadioButton *auto_radioButton = new QRadioButton(tr("Auto")); @@ -137,6 +142,10 @@ DsoTriggerDock::DsoTriggerDock(QWidget *parent, SigSession &session) : gLayout->addWidget(holdoff_comboBox, 12, 2); gLayout->addWidget(holdoff_slider, 13, 0, 1, 4); + gLayout->addWidget(new QLabel(_widget), 14, 0); + gLayout->addWidget(margin_label, 15, 0); + gLayout->addWidget(margin_slider, 16, 0, 1, 4); + gLayout->setColumnStretch(3, 1); layout->addLayout(gLayout); @@ -174,10 +183,7 @@ void DsoTriggerDock::pos_changed(int pos) msg.mBox()->setIcon(QMessageBox::Warning); msg.exec(); } - - uint64_t sample_limit = _session.get_device()->get_sample_limit(); - uint64_t trig_pos = sample_limit * pos / 100; - set_trig_pos(trig_pos); + set_trig_pos(pos); } void DsoTriggerDock::hold_changed(int hold) @@ -205,6 +211,22 @@ void DsoTriggerDock::hold_changed(int hold) } } +void DsoTriggerDock::margin_changed(int margin) +{ + int ret; + ret = _session.get_device()->set_config(NULL, NULL, + SR_CONF_TRIGGER_MARGIN, + g_variant_new_byte(margin)); + if (!ret) { + dialogs::DSMessageBox msg(this); + msg.mBox()->setText(tr("Trigger Setting Issue")); + msg.mBox()->setInformativeText(tr("Change trigger value sensitivity failed!")); + msg.mBox()->setStandardButtons(QMessageBox::Ok); + msg.mBox()->setIcon(QMessageBox::Warning); + msg.exec(); + } +} + void DsoTriggerDock::source_changed() { int id = source_group->checkedId(); @@ -254,6 +276,27 @@ void DsoTriggerDock::device_change() void DsoTriggerDock::init() { + if (_session.get_device()->name().contains("virtual")) { + foreach(QAbstractButton * btn, source_group->buttons()) + btn->setDisabled(true); + foreach(QAbstractButton * btn, type_group->buttons()) + btn->setDisabled(true); + holdoff_slider->setDisabled(true); + holdoff_spinBox->setDisabled(true); + holdoff_comboBox->setDisabled(true); + margin_slider->setDisabled(true); + return; + } else { + foreach(QAbstractButton * btn, source_group->buttons()) + btn->setDisabled(false); + foreach(QAbstractButton * btn, type_group->buttons()) + btn->setDisabled(false); + holdoff_slider->setDisabled(false); + holdoff_spinBox->setDisabled(false); + holdoff_comboBox->setDisabled(false); + margin_slider->setDisabled(false); + } + // TRIGGERPOS GVariant* gvar = _session.get_device()->get_config(NULL, NULL, SR_CONF_HORIZ_TRIGGERPOS); diff --git a/DSView/pv/dock/dsotriggerdock.h b/DSView/pv/dock/dsotriggerdock.h index a35521ed..d33045de 100644 --- a/DSView/pv/dock/dsotriggerdock.h +++ b/DSView/pv/dock/dsotriggerdock.h @@ -55,11 +55,12 @@ public: void init(); signals: - void set_trig_pos(quint64 trig_pos); + void set_trig_pos(int percent); private slots: void pos_changed(int pos); void hold_changed(int hold); + void margin_changed(int margin); void source_changed(); void type_changed(); @@ -74,6 +75,8 @@ private: QSpinBox *holdoff_spinBox; QSlider *holdoff_slider; + QSlider *margin_slider; + QSpinBox *position_spinBox; QSlider *position_slider; diff --git a/DSView/pv/mainframe.cpp b/DSView/pv/mainframe.cpp index 74f822a5..435f583e 100644 --- a/DSView/pv/mainframe.cpp +++ b/DSView/pv/mainframe.cpp @@ -54,6 +54,7 @@ MainFrame::MainFrame(DeviceManager &device_manager, _moving = false; _startPos = None; _freezing = false; + _minimized = false; // MainWindow _mainWindow = new MainWindow(device_manager, open_file_name, this); @@ -114,6 +115,33 @@ MainFrame::MainFrame(DeviceManager &device_manager, readSettings(); } +void MainFrame::changeEvent(QEvent* event) +{ + QFrame::changeEvent(event); + QWindowStateChangeEvent* win_event = static_cast< QWindowStateChangeEvent* >(event); + if(win_event->type() == QEvent::WindowStateChange) { + if (win_event->oldState() & Qt::WindowMinimized) { + if (_minimized) { + readSettings(); + _minimized = false; + } + } + } +} + + +void MainFrame::resizeEvent(QResizeEvent *event) +{ + QFrame::resizeEvent(event); + if (isMaximized()) { + hide_border(); + } else { + show_border(); + } + _titleBar->setRestoreButton(isMaximized()); + _layout->update(); +} + void MainFrame::closeEvent(QCloseEvent *event) { _mainWindow->session_save(); @@ -162,6 +190,13 @@ void MainFrame::showMaximized() QFrame::showMaximized(); } +void MainFrame::showMinimized() +{ + _minimized = true; + writeSettings(); + QFrame::showMinimized(); +} + bool MainFrame::eventFilter(QObject *object, QEvent *event) { const QEvent::Type type = event->type(); @@ -295,14 +330,6 @@ bool MainFrame::eventFilter(QObject *object, QEvent *event) } else if (!_draging && type == QEvent::Leave) { _startPos = None; setCursor(Qt::ArrowCursor); - } else if (type == QEvent::Resize) { - if (isMaximized()) { - hide_border(); - } else { - show_border(); - } - _titleBar->setRestoreButton(isMaximized()); - _layout->update(); } return QObject::eventFilter(object, event); @@ -326,9 +353,16 @@ void MainFrame::readSettings() QRect deskRect = desktopWidget->availableGeometry(); settings.beginGroup("MainFrame"); - resize(settings.value("size", QSize(minWidth, minHeight)).toSize()); - move(settings.value("pos", QPoint((deskRect.width() - minWidth)/2, (deskRect.height() - minHeight)/2)).toPoint()); + QSize size = settings.value("size", QSize(minWidth, minHeight)).toSize(); + QPoint pos = settings.value("pos", QPoint((deskRect.width() - minWidth)/2, (deskRect.height() - minHeight)/2)).toPoint(); settings.endGroup(); + + if (size == deskRect.size()) { + _titleBar->showMaxRestore(); + } else { + resize(size); + move(pos); + } } } // namespace pv diff --git a/DSView/pv/mainframe.h b/DSView/pv/mainframe.h index 50432639..65cab5a8 100644 --- a/DSView/pv/mainframe.h +++ b/DSView/pv/mainframe.h @@ -67,6 +67,8 @@ public: void showMaxRestore(); protected: + void changeEvent(QEvent* event); + void resizeEvent(QResizeEvent *event); void closeEvent(QCloseEvent *event); bool eventFilter(QObject *object, QEvent *event); @@ -74,6 +76,7 @@ public slots: void unfreezing(); void showNormal(); void showMaximized(); + void showMinimized(); private: void hide_border(); @@ -103,6 +106,7 @@ private: int _startPos; QTimer _timer; bool _freezing; + bool _minimized; }; } // namespace pv diff --git a/DSView/pv/mainwindow.cpp b/DSView/pv/mainwindow.cpp index 4bf1f35b..ad024189 100644 --- a/DSView/pv/mainwindow.cpp +++ b/DSView/pv/mainwindow.cpp @@ -199,8 +199,8 @@ void MainWindow::setup_ui() SLOT(show_calibration())); connect(_sampling_bar, SIGNAL(hide_calibration()), _view, SLOT(hide_calibration())); - connect(_dso_trigger_widget, SIGNAL(set_trig_pos(quint64)), _view, - SLOT(set_trig_pos(quint64))); + connect(_dso_trigger_widget, SIGNAL(set_trig_pos(int)), _view, + SLOT(set_trig_pos(int))); connect(_protocol_widget, SIGNAL(protocol_updated()), _view, SLOT(signals_changed())); setIconSize(QSize(40,40)); @@ -318,6 +318,7 @@ void MainWindow::update_device_list() } if (!selected_device->name().contains("virtual")) { + _file_bar->set_settings_en(true); _logo_bar->dsl_connected(true); QString ses_name = DS_RES_PATH + selected_device->name() + @@ -325,9 +326,12 @@ void MainWindow::update_device_list() ".dsc"; load_session(ses_name); } else { + _file_bar->set_settings_en(false); _logo_bar->dsl_connected(false); } _view->status_clear(); + _trigger_widget->init(); + _dso_trigger_widget->init(); } void MainWindow::reload() @@ -416,7 +420,10 @@ void MainWindow::run_stop() switch(_session.get_capture_state()) { case SigSession::Init: case SigSession::Stopped: - _view->show_trig_cursor(false); + if (_session.get_device()->dev_inst()->mode == DSO) + _view->show_trig_cursor(true); + else + _view->show_trig_cursor(false); _view->update_sample(false); commit_trigger(false); _session.start_capture(false, @@ -436,7 +443,10 @@ void MainWindow::instant_stop() switch(_session.get_capture_state()) { case SigSession::Init: case SigSession::Stopped: - _view->show_trig_cursor(false); + if (_session.get_device()->dev_inst()->mode == DSO) + _view->show_trig_cursor(true); + else + _view->show_trig_cursor(false); _view->update_sample(true); commit_trigger(true); _session.start_capture(true, @@ -650,6 +660,8 @@ bool MainWindow::load_session(QString name) for (unsigned int i = 0; i < num_opts; i++) { const struct sr_config_info *const info = sr_config_info_get(options[i]); + if (!sessionObj.contains(info->name)) + continue; if (info->datatype == SR_T_BOOL) _session.get_device()->set_config(NULL, NULL, info->key, g_variant_new_boolean(sessionObj[info->name].toDouble())); else if (info->datatype == SR_T_UINT64) @@ -709,8 +721,8 @@ bool MainWindow::load_session(QString name) boost::shared_ptr dsoSig; if (dsoSig = dynamic_pointer_cast(s)) { dsoSig->load_settings(); - dsoSig->set_zeroRate(obj["zeroPos"].toDouble()); - dsoSig->set_trigRate(obj["trigValue"].toDouble()); + dsoSig->set_zero_vrate(obj["zeroPos"].toDouble()); + dsoSig->set_trig_vrate(obj["trigValue"].toDouble()); } break; } @@ -791,8 +803,8 @@ bool MainWindow::store_session(QString name) s_obj["vdiv"] = QJsonValue::fromVariant(static_cast(dsoSig->get_vDialValue())); s_obj["vfactor"] = QJsonValue::fromVariant(static_cast(dsoSig->get_factor())); s_obj["coupling"] = dsoSig->get_acCoupling(); - s_obj["trigValue"] = dsoSig->get_trigRate(); - s_obj["zeroPos"] = dsoSig->get_zeroRate(); + s_obj["trigValue"] = dsoSig->get_trig_vrate(); + s_obj["zeroPos"] = dsoSig->get_zero_vrate(); } channelVar.append(s_obj); } diff --git a/DSView/pv/sigsession.cpp b/DSView/pv/sigsession.cpp index 1c9e7f1e..a3ae90d3 100644 --- a/DSView/pv/sigsession.cpp +++ b/DSView/pv/sigsession.cpp @@ -98,6 +98,7 @@ SigSession::SigSession(DeviceManager &device_manager) : _group_cnt = 0; register_hotplug_callback(); _view_timer.stop(); + _noData_cnt = 0; _refresh_timer.stop(); _refresh_timer.setSingleShot(true); _data_lock = false; @@ -470,12 +471,48 @@ double SigSession::cur_sampletime() const return _cur_samplelimits * 1.0 / _cur_samplerate; } +void SigSession::set_cur_samplerate(uint64_t samplerate) +{ + assert(samplerate != 0); + _cur_samplerate = samplerate; + // sample rate for all SignalData + // Logic/Analog/Dso + if (_logic_data) + _logic_data->set_samplerate(_cur_samplerate); + if (_analog_data) + _analog_data->set_samplerate(_cur_samplerate); + if (_dso_data) + _dso_data->set_samplerate(_cur_samplerate); + // Group + if (_group_data) + _group_data->set_samplerate(_cur_samplerate); + +#ifdef ENABLE_DECODE + // DecoderStack + BOOST_FOREACH(const boost::shared_ptr d, _decode_traces) + d->decoder()->set_samplerate(_cur_samplerate); +#endif + // MathStack + BOOST_FOREACH(const boost::shared_ptr m, _math_traces) + m->get_math_stack()->set_samplerate(_cur_samplerate); +} + +void SigSession::set_cur_samplelimits(uint64_t samplelimits) +{ + assert(samplelimits != 0); + _cur_samplelimits = samplelimits; +} + + void SigSession::capture_init() { _cur_samplerate = _dev_inst->get_sample_rate(); _cur_samplelimits = _dev_inst->get_sample_limit(); _data_updated = false; - _view_timer.start(ViewTime); + if (_dev_inst->dev_inst()->mode == DSO) { + _view_timer.start(ViewTime); + _noData_cnt = 0; + } // Init and Set sample rate for all SignalData // Logic/Analog/Dso @@ -685,6 +722,10 @@ void SigSession::check_update() if (_data_updated) { data_updated(); _data_updated = false; + _noData_cnt = 0; + } else { + if (++_noData_cnt >= (WaitShowTime/ViewTime)) + show_wait_trigger(); } } diff --git a/DSView/pv/sigsession.h b/DSView/pv/sigsession.h index d046cbde..fe450369 100644 --- a/DSView/pv/sigsession.h +++ b/DSView/pv/sigsession.h @@ -92,10 +92,13 @@ class SigSession : public QObject private: static constexpr float Oversampling = 2.0f; - static const int ViewTime = 50; static const int RefreshTime = 500; bool saveFileThreadRunning = false; +public: + static const int ViewTime = 50; + static const int WaitShowTime = 1000; + public: enum capture_state { Init, @@ -131,6 +134,8 @@ public: uint64_t cur_samplerate() const; uint64_t cur_samplelimits() const; double cur_sampletime() const; + void set_cur_samplerate(uint64_t samplerate); + void set_cur_samplelimits(uint64_t samplelimits); QDateTime get_trigger_time() const; uint64_t get_trigger_pos() const; @@ -272,6 +277,7 @@ private: bool _hot_detach; QTimer _view_timer; + int _noData_cnt; QTimer _refresh_timer; bool _data_lock; bool _data_updated; @@ -320,6 +326,8 @@ signals: void hardware_connect_failed(); + void show_wait_trigger(); + public slots: void reload(); void refresh(int holdtime); diff --git a/DSView/pv/toolbars/filebar.cpp b/DSView/pv/toolbars/filebar.cpp index e76c354c..89e5fd6f 100644 --- a/DSView/pv/toolbars/filebar.cpp +++ b/DSView/pv/toolbars/filebar.cpp @@ -69,7 +69,7 @@ FileBar::FileBar(SigSession &session, QWidget *parent) : _action_default->setObjectName(QString::fromUtf8("actionDefault")); connect(_action_default, SIGNAL(triggered()), this, SLOT(on_actionDefault_triggered())); - _menu_session = new QMenu(tr("Session"), parent); + _menu_session = new QMenu(tr("Settings"), parent); _menu_session->setIcon(QIcon::fromTheme("file", QIcon(":/icons/gear.png"))); _menu_session->setObjectName(QString::fromUtf8("menuSession")); @@ -287,5 +287,10 @@ void FileBar::enable_toggle(bool enable) QIcon(":/icons/file_dis.png")); } +void FileBar::set_settings_en(bool enable) +{ + _menu_session->setDisabled(!enable); +} + } // namespace toolbars } // namespace pv diff --git a/DSView/pv/toolbars/filebar.h b/DSView/pv/toolbars/filebar.h index a77f3928..a69f3147 100644 --- a/DSView/pv/toolbars/filebar.h +++ b/DSView/pv/toolbars/filebar.h @@ -42,6 +42,8 @@ public: void enable_toggle(bool enable); + void set_settings_en(bool enable); + private: void session_error( diff --git a/DSView/pv/toolbars/titlebar.cpp b/DSView/pv/toolbars/titlebar.cpp index 942a3fd8..64ccd572 100644 --- a/DSView/pv/toolbars/titlebar.cpp +++ b/DSView/pv/toolbars/titlebar.cpp @@ -66,7 +66,7 @@ TitleBar::TitleBar(bool top, QWidget *parent, bool hasClose) : connect(this, SIGNAL( normalShow() ), parent, SLOT(showNormal() ) ); connect(this, SIGNAL( maximizedShow() ), parent, SLOT(showMaximized() ) ); - connect(_minimizeButton, SIGNAL( clicked() ), this, SLOT(showSmall() ) ); + connect(_minimizeButton, SIGNAL( clicked() ), parent, SLOT(showMinimized() ) ); connect(_maximizeButton, SIGNAL( clicked() ), this, SLOT(showMaxRestore() ) ); } diff --git a/DSView/pv/toolbars/titlebar.h b/DSView/pv/toolbars/titlebar.h index d0c785d7..6004411a 100644 --- a/DSView/pv/toolbars/titlebar.h +++ b/DSView/pv/toolbars/titlebar.h @@ -44,7 +44,6 @@ signals: void maximizedShow(); public slots: - void showSmall() { parentWidget()->showMinimized(); } void showMaxRestore(); void setRestoreButton(bool max); diff --git a/DSView/pv/view/dsosignal.cpp b/DSView/pv/view/dsosignal.cpp index 7f4b37ac..470f6965 100644 --- a/DSView/pv/view/dsosignal.cpp +++ b/DSView/pv/view/dsosignal.cpp @@ -104,11 +104,6 @@ const QColor DsoSignal::SignalColours[4] = { }; const float DsoSignal::EnvelopeThreshold = 256.0f; -const double DsoSignal::TrigMargin = 0.02; - -const int DsoSignal::UpMargin = 30; -const int DsoSignal::DownMargin = 0; -const int DsoSignal::RightMargin = 30; DsoSignal::DsoSignal(boost::shared_ptr dev_inst, boost::shared_ptr data, @@ -118,9 +113,6 @@ DsoSignal::DsoSignal(boost::shared_ptr dev_inst, _scale(0), _vDialActive(false), _hDialActive(false), - //_trig_vpos(probe->index * 0.5 + 0.25), - //_zeroPos(probe->index * 0.5 + 0.25) - _trig_vpos(0.5), _autoV(false), _autoH(false), _hover_en(false), @@ -176,7 +168,6 @@ boost::shared_ptr DsoSignal::dso_data() const void DsoSignal::set_viewport(pv::view::Viewport *viewport) { Trace::set_viewport(viewport); - update_zeroPos(); const double ms_left = get_view_rect().right() - (MS_RectWidth + MS_RectMargin) * (get_index() + 1); const double ms_top = get_view_rect().top() + 5; @@ -262,7 +253,7 @@ bool DsoSignal::go_vDialPre() g_variant_new_uint64(_vDial->get_value())); if (_view->session().get_capture_state() == SigSession::Stopped) _scale *= pre_vdiv/_vDial->get_value(); - update_zeroPos(); + update_offset(); _view->update_calibration(); _view->set_update(_viewport, true); _view->update(); @@ -282,7 +273,7 @@ bool DsoSignal::go_vDialNext() g_variant_new_uint64(_vDial->get_value())); if (_view->session().get_capture_state() == SigSession::Stopped) _scale *= pre_vdiv/_vDial->get_value(); - update_zeroPos(); + update_offset(); _view->update_calibration(); _view->set_update(_viewport, true); _view->update(); @@ -443,6 +434,15 @@ bool DsoSignal::load_settings() // qDebug() << "ERROR: config_get SR_CONF_EN_CH failed."; // return false; //} + gvar = _dev_inst->get_config(_probe, NULL, SR_CONF_DSO_BITS); + if (gvar != NULL) { + _bits = g_variant_get_byte(gvar); + g_variant_unref(gvar); + } else { + _bits = DefaultBits; + qDebug("Warning: config_get SR_CONF_DSO_BITS failed, set to %d(default).", DefaultBits); + return false; + } // -- hdiv uint64_t hdiv; @@ -511,25 +511,19 @@ bool DsoSignal::load_settings() qDebug() << "ERROR: config_get SR_CONF_VPOS failed."; return false; } - _zeroPos = min(max((0.5 - vpos / (_vDial->get_value() * DS_CONF_DSO_VDIVS)), 0.0), 1.0); - _zero_off = _zeroPos * 255; + _zero_vrate = min(max((0.5 - vpos / (_vDial->get_value() * DS_CONF_DSO_VDIVS)), 0.0), 1.0); + _zero_value = _zero_vrate * ((1 << _bits) - 1); // -- trig_value - uint8_t trigger_value; gvar = _dev_inst->get_config(_probe, NULL, SR_CONF_TRIGGER_VALUE); if (gvar != NULL) { - trigger_value = g_variant_get_byte(gvar); + _trig_value = g_variant_get_byte(gvar); + _trig_delta = get_trig_vrate() - _zero_vrate; g_variant_unref(gvar); } else { qDebug() << "ERROR: config_get SR_CONF_TRIGGER_VALUE failed."; return false; } - bool isDSCope = (_dev_inst->name() == "DSCope"); - if (isDSCope) { - _trig_vpos = min(max(trigger_value/255.0, 0+TrigMargin), 1-TrigMargin); - } else { - _trig_vpos = min(max(_zeroPos + ((trigger_value - 0x80) / 255.0), 0+TrigMargin), 1-TrigMargin); - } if (_view) { _view->set_update(_viewport, true); @@ -560,7 +554,7 @@ int DsoSignal::commit_settings() g_variant_new_byte(_acCoupling)); // -- vpos - double vpos_off = (0.5 - (get_zeroPos() - UpMargin) * 1.0/get_view_rect().height()) * _vDial->get_value() * DS_CONF_DSO_VDIVS; + double vpos_off = (0.5 - (get_zero_vpos() - UpMargin) * 1.0/get_view_rect().height()) * _vDial->get_value() * DS_CONF_DSO_VDIVS; ret = _dev_inst->set_config(_probe, NULL, SR_CONF_VPOS, g_variant_new_double(vpos_off)); @@ -603,88 +597,82 @@ void DsoSignal::set_acCoupling(uint8_t coupling) int DsoSignal::get_trig_vpos() const { - return _trig_vpos * get_view_rect().height() + UpMargin; + return get_trig_vrate() * get_view_rect().height() + UpMargin; } -double DsoSignal::get_trigRate() const +double DsoSignal::get_trig_vrate() const { - return _trig_vpos; + return _trig_value * 1.0 / ((1 << _bits) - 1.0); } -void DsoSignal::set_trig_vpos(int pos) +void DsoSignal::set_trig_vpos(int pos, bool delta_change) { assert(_view); - int trig_value; if (enabled()) { - double delta = min((double)max(pos - UpMargin, 0), get_view_rect().height()) * 1.0 / get_view_rect().height(); - bool isDSCope = (_dev_inst->name() == "DSCope"); - if (isDSCope) { - trig_value = delta * 255.0 + 0.5; - _trig_vpos = min(max(trig_value/255.0, 0+TrigMargin), 1-TrigMargin); - } else { - delta = delta - _zeroPos; + double delta = min(max(pos - UpMargin, 0), get_view_rect().height()) * 1.0 / get_view_rect().height(); + if (_dev_inst->name() == "DSLogic") { + delta = delta - _zero_vrate; delta = min(delta, 0.5); delta = max(delta, -0.5); - trig_value = (delta * 255.0f + 0x80); - _trig_vpos = min(max(_zeroPos + (trig_value - 0x80) / 255.0, 0+TrigMargin), 1-TrigMargin); + _trig_value = delta * ((1 << _bits) -1) + (1 << (_bits - 1)); + } else { + _trig_value = delta * ((1 << _bits) -1) + 0.5; } + int margin = TrigMargin; + _trig_value = std::min(std::max(_trig_value, margin), ((1 << _bits) - margin - 1)); + if (delta_change) + _trig_delta = get_trig_vrate() - _zero_vrate; _dev_inst->set_config(_probe, NULL, SR_CONF_TRIGGER_VALUE, - g_variant_new_byte(trig_value)); + g_variant_new_byte(_trig_value)); } } -void DsoSignal::set_trigRate(double rate) +void DsoSignal::set_trig_vrate(double rate) { - int trig_value; double delta = rate; - bool isDSCope = (_dev_inst->name() == "DSCope"); - if (isDSCope) { - trig_value = delta * 255.0 + 0.5; - _trig_vpos = min(max(trig_value/255.0, 0+TrigMargin), 1-TrigMargin); - - } else { - delta = delta - _zeroPos; + if (_dev_inst->name() == "DSLogic") { + delta = delta - _zero_vrate; delta = min(delta, 0.5); delta = max(delta, -0.5); - trig_value = (delta * 255.0f + 0x80); - _trig_vpos = min(max(_zeroPos + (trig_value - 0x80) / 255.0, 0+TrigMargin), 1-TrigMargin); + _trig_value = delta * ((1 << _bits) - 1) + (1 << (_bits - 1)); + } else { + _trig_value = delta * ((1 << _bits) - 1) + 0.5; } + _trig_delta = get_trig_vrate() - _zero_vrate; _dev_inst->set_config(_probe, NULL, SR_CONF_TRIGGER_VALUE, - g_variant_new_byte(trig_value)); + g_variant_new_byte(_trig_value)); } -int DsoSignal::get_zeroPos() +int DsoSignal::get_zero_vpos() { - return _zeroPos * get_view_rect().height() + UpMargin; + return _zero_vrate * get_view_rect().height() + UpMargin; } -double DsoSignal::get_zeroRate() +double DsoSignal::get_zero_vrate() { - return _zeroPos; + return _zero_vrate; } -double DsoSignal::get_zeroValue() +double DsoSignal::get_zero_value() { - return _zero_off; + return _zero_value; } -void DsoSignal::set_zeroPos(int pos) +void DsoSignal::set_zero_vpos(int pos) { if (enabled()) { - double delta = _trig_vpos - _zeroPos; - set_trig_vpos(get_trig_vpos() + pos - get_zeroPos()); - _zeroPos = min((double)max(pos - UpMargin, 0), get_view_rect().height()) * 1.0 / get_view_rect().height(); - _trig_vpos = min(max(_zeroPos + delta, 0+TrigMargin), 1-TrigMargin); - - update_zeroPos(); + double delta = _trig_delta* get_view_rect().height(); + _zero_vrate = min(max(pos - UpMargin, 0), get_view_rect().height()) * 1.0 / get_view_rect().height(); + set_trig_vpos(get_zero_vpos() + delta, false); + update_offset(); } } -void DsoSignal::set_zeroRate(double rate) +void DsoSignal::set_zero_vrate(double rate) { - _zeroPos = rate; - _zero_off = rate * 255; - update_zeroPos(); + _zero_vrate = rate; + _zero_value = rate * ((1 << _bits) - 1); + update_offset(); } void DsoSignal::set_factor(uint64_t factor) @@ -779,20 +767,17 @@ QString DsoSignal::get_ms_string(int index) const } } -void DsoSignal::update_zeroPos() +void DsoSignal::update_offset() { - if (_dev_inst->name() == "DSCope") { - //double vpos_off = (0.5 - _zeroPos) * _vDial->get_value() * DS_CONF_DSO_VDIVS; - double vpos_off = (0.5 - (get_zeroPos() - UpMargin) * 1.0/get_view_rect().height()) * _vDial->get_value() * DS_CONF_DSO_VDIVS; - _dev_inst->set_config(_probe, NULL, SR_CONF_VPOS, - g_variant_new_double(vpos_off)); - } + double vpos_off = (0.5 - _zero_vrate) * _vDial->get_value() * DS_CONF_DSO_VDIVS; + _dev_inst->set_config(_probe, NULL, SR_CONF_VPOS, + g_variant_new_double(vpos_off)); } -QRectF DsoSignal::get_view_rect() const +QRect DsoSignal::get_view_rect() const { assert(_viewport); - return QRectF(0, UpMargin, + return QRect(0, UpMargin, _viewport->width() - RightMargin, _viewport->height() - UpMargin - DownMargin); } @@ -871,7 +856,7 @@ void DsoSignal::paint_mid(QPainter &p, int left, int right) const int height = get_view_rect().height(); const int width = right - left; - const int y = get_zeroPos() + height * 0.5; + const int y = get_zero_vpos() + height * 0.5; const double scale = _view->scale(); assert(scale > 0); const double offset = _view->offset(); @@ -927,7 +912,7 @@ void DsoSignal::paint_fore(QPainter &p, int left, int right) QPen pen(Signal::dsGray); pen.setStyle(Qt::DotLine); p.setPen(pen); - p.drawLine(left, get_zeroPos(), right, get_zeroPos()); + p.drawLine(left, get_zero_vpos(), right, get_zero_vpos()); if(enabled()) { const QPointF mouse_point = _view->hover_point(); @@ -949,7 +934,7 @@ void DsoSignal::paint_fore(QPainter &p, int left, int right) // paint the trig voltage int trigp = get_trig_vpos(); - float t_vol = (_zeroPos - _trig_vpos) * _vDial->get_value() * _vDial->get_factor() * DS_CONF_DSO_VDIVS; + float t_vol = (_zero_vrate - get_trig_vrate()) * _vDial->get_value() * _vDial->get_factor() * DS_CONF_DSO_VDIVS; QString t_vol_s = (_vDial->get_value() >= 500) ? QString::number(t_vol/1000.0f, 'f', 2)+"V" : QString::number(t_vol, 'f', 2)+"mV"; int vol_width = p.boundingRect(0, 0, INT_MAX, INT_MAX, Qt::AlignLeft | Qt::AlignTop, t_vol_s).width(); @@ -998,10 +983,10 @@ void DsoSignal::paint_trace(QPainter &p, float top = get_view_rect().top(); float bottom = get_view_rect().bottom(); - float zeroP = _zeroPos * get_view_rect().height() + top;; + float zeroP = _zero_vrate * get_view_rect().height() + top;; if (_dev_inst->name() == "DSCope" && _view->session().get_capture_state() == SigSession::Running) - _zero_off = _zeroPos * 255; + _zero_value = _zero_vrate * ((1 << _bits) - 1); float x = (start / samples_per_pixel - pixels_offset) + left; double pixels_per_sample = 1.0/samples_per_pixel; uint8_t offset; @@ -1012,7 +997,7 @@ void DsoSignal::paint_trace(QPainter &p, //offset = samples[(sample - start)*num_channels]; offset = samples[sample]; - const float y = min(max(top, zeroP + (offset - _zero_off) * _scale), bottom); + const float y = min(max(top, zeroP + (offset - _zero_value) * _scale), bottom); *point++ = QPointF(x, y); x += pixels_per_sample; //*point++ = QPointF(x, top + offset); @@ -1054,10 +1039,10 @@ void DsoSignal::paint_envelope(QPainter &p, QRectF *rect = rects; float top = get_view_rect().top(); float bottom = get_view_rect().bottom(); - float zeroP = _zeroPos * get_view_rect().height() + top; + float zeroP = _zero_vrate * get_view_rect().height() + top; if (_dev_inst->name() == "DSCope" && _view->session().get_capture_state() == SigSession::Running) - _zero_off = _zeroPos * 255; + _zero_value = _zero_vrate * ((1 << _bits) - 1); for(uint64_t sample = 0; sample < e.length-1; sample++) { const float x = ((e.scale * sample + e.start) / samples_per_pixel - pixels_offset) + left; @@ -1066,8 +1051,8 @@ void DsoSignal::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 = min(max(top, ((max(s->max, (s+1)->min) - _zero_off) * _scale + zeroP)), bottom); - const float t = min(max(top, ((min(s->min, (s+1)->max) - _zero_off) * _scale + zeroP)), bottom); + const float b = min(max(top, ((max(s->max, (s+1)->min) - _zero_value) * _scale + zeroP)), bottom); + const float t = min(max(top, ((min(s->min, (s+1)->max) - _zero_value) * _scale + zeroP)), bottom); float h = b - t; if(h >= 0.0f && h <= 1.0f) @@ -1131,23 +1116,23 @@ void DsoSignal::paint_type_options(QPainter &p, int right, const QPoint pt) return; } - p.setPen(Qt::white); + p.setPen(Qt::transparent); p.setBrush((enabled() && (factor == 100)) ? (x100_rect.contains(pt) ? _colour.darker() : _colour) : (x100_rect.contains(pt) ? _colour.darker() : dsDisable)); p.drawRect(x100_rect); - p.drawText(x100_rect, Qt::AlignCenter | Qt::AlignVCenter, "x100"); - p.setBrush((enabled() && (factor == 10)) ? (x10_rect.contains(pt) ? _colour.darker() : _colour) : (x10_rect.contains(pt) ? _colour.darker() : dsDisable)); p.drawRect(x10_rect); - p.drawText(x10_rect, Qt::AlignCenter | Qt::AlignVCenter, "x10"); - p.setBrush((enabled() && (factor == 1)) ? (x1_rect.contains(pt) ? _colour.darker() : _colour) : (x1_rect.contains(pt) ? _colour.darker() : dsDisable)); p.drawRect(x1_rect); + + p.setPen(Qt::white); + p.drawText(x100_rect, Qt::AlignCenter | Qt::AlignVCenter, "x100"); + p.drawText(x10_rect, Qt::AlignCenter | Qt::AlignVCenter, "x10"); p.drawText(x1_rect, Qt::AlignCenter | Qt::AlignVCenter, "x1"); } bool DsoSignal::mouse_double_click(int right, const QPoint pt) { - int y = get_zeroPos(); + int y = get_zero_vpos(); const QRectF label_rect = Trace::get_rect("label", y, right); if (label_rect.contains(pt)) { this->auto_set(); @@ -1326,8 +1311,8 @@ void DsoSignal::paint_measure(QPainter &p) _min = (index == 0) ? status.ch0_min : status.ch1_min; const uint64_t period = (index == 0) ? status.ch0_period : status.ch1_period; const uint32_t count = (index == 0) ? status.ch0_pcnt : status.ch1_pcnt; - double value_max = (_zero_off - _min) * _scale * _vDial->get_value() * _vDial->get_factor() * DS_CONF_DSO_VDIVS / get_view_rect().height(); - double value_min = (_zero_off - _max) * _scale * _vDial->get_value() * _vDial->get_factor() * DS_CONF_DSO_VDIVS / get_view_rect().height(); + double value_max = (_zero_value - _min) * _scale * _vDial->get_value() * _vDial->get_factor() * DS_CONF_DSO_VDIVS / get_view_rect().height(); + double value_min = (_zero_value - _max) * _scale * _vDial->get_value() * _vDial->get_factor() * DS_CONF_DSO_VDIVS / get_view_rect().height(); double value_p2p = value_max - value_min; _period = (count == 0) ? period * 10.0 : period * 10.0 / count; const int channel_count = _view->session().get_ch_num(SR_CHANNEL_DSO); @@ -1348,7 +1333,7 @@ void DsoSignal::paint_measure(QPainter &p) if (!snapshots.empty()) { const boost::shared_ptr &snapshot = snapshots.front(); - const double vrms = snapshot->cal_vrms(_zero_off, get_index()); + const double vrms = snapshot->cal_vrms(_zero_value, get_index()); const double value_vrms = vrms * _scale * _vDial->get_value() * _vDial->get_factor() * DS_CONF_DSO_VDIVS / get_view_rect().height(); _ms_string[DSO_MS_VRMS] = "Vrms: " + (abs(value_vrms) > 1000 ? QString::number(value_vrms/1000.0, 'f', 2) + "V" : QString::number(value_vrms, 'f', 2) + "mV"); } @@ -1361,7 +1346,7 @@ void DsoSignal::paint_measure(QPainter &p) const boost::shared_ptr &snapshot = snapshots.front(); const double vmean = snapshot->cal_vmean(get_index()); - const double value_vmean = (_zero_off - vmean) * _scale * _vDial->get_value() * _vDial->get_factor() * DS_CONF_DSO_VDIVS / get_view_rect().height(); + const double value_vmean = (_zero_value - vmean) * _scale * _vDial->get_value() * _vDial->get_factor() * DS_CONF_DSO_VDIVS / get_view_rect().height(); _ms_string[DSO_MS_VMEA] = "Vmean: " + (abs(value_vmean) > 1000 ? QString::number(value_vmean/1000.0, 'f', 2) + "V" : QString::number(value_vmean, 'f', 2) + "mV"); } } @@ -1527,17 +1512,17 @@ bool DsoSignal::measure(const QPointF &p) const uint8_t cur_sample = *snapshot->get_samples(_hover_index, _hover_index, get_index()); const uint8_t nxt_sample = *snapshot->get_samples(nxt_index, nxt_index, get_index()); - _hover_value = (_zero_off - cur_sample) * _scale * _vDial->get_value() * _vDial->get_factor() * DS_CONF_DSO_VDIVS / get_view_rect().height(); + _hover_value = (_zero_value - cur_sample) * _scale * _vDial->get_value() * _vDial->get_factor() * DS_CONF_DSO_VDIVS / get_view_rect().height(); float top = get_view_rect().top(); float bottom = get_view_rect().bottom(); - float zeroP = _zeroPos * get_view_rect().height() + top; + float zeroP = _zero_vrate * get_view_rect().height() + top; float pre_x = (pre_index / samples_per_pixel - pixels_offset); - const float pre_y = min(max(top, zeroP + (pre_sample - _zero_off)* _scale), bottom); + const float pre_y = min(max(top, zeroP + (pre_sample - _zero_value)* _scale), bottom); float x = (_hover_index / samples_per_pixel - pixels_offset); - const float y = min(max(top, zeroP + (cur_sample - _zero_off)* _scale), bottom); + const float y = min(max(top, zeroP + (cur_sample - _zero_value)* _scale), bottom); float nxt_x = (nxt_index / samples_per_pixel - pixels_offset); - const float nxt_y = min(max(top, zeroP + (nxt_sample - _zero_off)* _scale), bottom); + const float nxt_y = min(max(top, zeroP + (nxt_sample - _zero_value)* _scale), bottom); const QRectF slope_rect = QRectF(QPointF(pre_x - 10, pre_y - 10), QPointF(nxt_x + 10, nxt_y + 10)); if (abs(y-p.y()) < 20 || slope_rect.contains(p)) { _hover_point = QPointF(x, y); diff --git a/DSView/pv/view/dsosignal.h b/DSView/pv/view/dsosignal.h index 69ebcd4b..dbe6cd3c 100644 --- a/DSView/pv/view/dsosignal.h +++ b/DSView/pv/view/dsosignal.h @@ -43,7 +43,6 @@ class DsoSignal : public Signal private: static const QColor SignalColours[4]; static const float EnvelopeThreshold; - static const double TrigMargin; static const int HitCursorMargin = 3; static const uint64_t vDialValueCount = 8; @@ -58,9 +57,12 @@ private: static const uint64_t hDialValue[hDialValueCount]; static const QString hDialUnit[hDialUnitCount]; - static const int UpMargin; - static const int DownMargin; - static const int RightMargin; + static const int UpMargin = 30; + static const int DownMargin = 0; + static const int RightMargin = 30; + + static const uint8_t DefaultBits = 8; + static const int TrigMargin = 16; public: enum DSO_MEASURE_TYPE { @@ -130,10 +132,10 @@ public: uint16_t get_hDialSel() const; uint8_t get_acCoupling() const; void set_acCoupling(uint8_t coupling); - void set_trig_vpos(int pos); + void set_trig_vpos(int pos, bool delta_change); int get_trig_vpos() const; - void set_trigRate(double rate); - double get_trigRate() const; + void set_trig_vrate(double rate); + double get_trig_vrate() const; void set_factor(uint64_t factor); uint64_t get_factor(); @@ -154,15 +156,15 @@ public: /** * Gets the mid-Y position of this signal. */ - int get_zeroPos(); - double get_zeroRate(); - double get_zeroValue(); + int get_zero_vpos(); + double get_zero_vrate(); + double get_zero_value(); /** * Sets the mid-Y position of this signal. */ - void set_zeroPos(int pos); - void set_zeroRate(double rate); - void update_zeroPos(); + void set_zero_vpos(int pos); + void set_zero_vrate(double rate); + void update_offset(); /** * Paints the background layer of the trace with a QPainter @@ -190,7 +192,7 @@ public: const std::vector< std::pair > cur_edges() const; - QRectF get_view_rect() const; + QRect get_view_rect() const; QRectF get_trig_rect(int left, int right) const; @@ -237,10 +239,12 @@ private: bool _vDialActive; bool _hDialActive; uint8_t _acCoupling; + uint8_t _bits; - double _trig_vpos; - double _zeroPos; - float _zero_off; + int _trig_value; + double _trig_delta; + double _zero_vrate; + float _zero_value; uint8_t _max; uint8_t _min; diff --git a/DSView/pv/view/header.cpp b/DSView/pv/view/header.cpp index b358c202..b47c2370 100644 --- a/DSView/pv/view/header.cpp +++ b/DSView/pv/view/header.cpp @@ -198,7 +198,7 @@ void Header::mousePressEvent(QMouseEvent *event) // Add the Trace to the drag list if (event->button() & Qt::LeftButton) { - _drag_traces.push_back(make_pair(mTrace, mTrace->get_zeroPos())); + _drag_traces.push_back(make_pair(mTrace, mTrace->get_zero_vpos())); } } mTrace->set_old_v_offset(mTrace->get_v_offset()); @@ -315,7 +315,7 @@ void Header::mouseMoveEvent(QMouseEvent *event) } else { boost::shared_ptr dsoSig; if (dsoSig = dynamic_pointer_cast(sig)) { - dsoSig->set_zeroPos(y); + dsoSig->set_zero_vpos(y); dsoSig->select(false); traces_moved(); } diff --git a/DSView/pv/view/mathtrace.cpp b/DSView/pv/view/mathtrace.cpp index b19ea97e..0dad2305 100644 --- a/DSView/pv/view/mathtrace.cpp +++ b/DSView/pv/view/mathtrace.cpp @@ -217,13 +217,13 @@ QString MathTrace::format_freq(double freq, unsigned precision) } } -bool MathTrace::measure(const QPointF &p) +bool MathTrace::measure(const QPoint &p) { _hover_en = false; if(!_view || !enabled()) return false; - const QRectF window = get_view_rect(); + const QRect window = get_view_rect(); if (!window.contains(p)) return false; @@ -291,14 +291,12 @@ void MathTrace::paint_mid(QPainter &p, int left, int right) double vdiv; double vfactor; - double voffset; BOOST_FOREACH(const boost::shared_ptr s, _session.get_signals()) { boost::shared_ptr dsoSig; if (dsoSig = dynamic_pointer_cast(s)) { if(dsoSig->get_index() == _math_stack->get_index()) { vdiv = dsoSig->get_vDialValue(); vfactor = dsoSig->get_factor(); - voffset = dsoSig->get_zeroRate(); break; } } @@ -476,10 +474,10 @@ void MathTrace::paint_type_options(QPainter &p, int right, const QPoint pt) (void)right; } -QRectF MathTrace::get_view_rect() const +QRect MathTrace::get_view_rect() const { assert(_viewport); - return QRectF(0, UpMargin, + return QRect(0, UpMargin, _viewport->width() - RightMargin, _viewport->height() - UpMargin - DownMargin); } diff --git a/DSView/pv/view/mathtrace.h b/DSView/pv/view/mathtrace.h index 7fac9552..7bb39783 100644 --- a/DSView/pv/view/mathtrace.h +++ b/DSView/pv/view/mathtrace.h @@ -95,7 +95,7 @@ public: static QString format_freq(double freq, unsigned precision = Pricision); - bool measure(const QPointF &p); + bool measure(const QPoint &p); /** * Paints the background layer of the trace with a QPainter @@ -121,7 +121,7 @@ public: **/ void paint_fore(QPainter &p, int left, int right); - QRectF get_view_rect() const; + QRect get_view_rect() const; protected: void paint_type_options(QPainter &p, int right, const QPoint pt); diff --git a/DSView/pv/view/timemarker.cpp b/DSView/pv/view/timemarker.cpp index 1956f013..d6eb74f9 100644 --- a/DSView/pv/view/timemarker.cpp +++ b/DSView/pv/view/timemarker.cpp @@ -70,8 +70,10 @@ void TimeMarker::set_index(uint64_t index) void TimeMarker::paint(QPainter &p, const QRect &rect, const bool highlight) { - const double samples_per_pixel = _view.session().cur_samplerate() * _view.scale(); - const double x = _index/samples_per_pixel - (_view.offset() / _view.scale()); + const uint64_t sample_rate = _view.session().cur_samplerate(); + const double scale = _view.scale(); + const double samples_per_pixel = sample_rate * scale; + const double x = _index/samples_per_pixel - (_view.offset() / scale); p.setPen((_grabbed | highlight) ? QPen(_colour.lighter(), 2, Qt::DashLine) : QPen(_colour, 1, Qt::DashLine)); p.drawLine(QPointF(x, rect.top()), QPointF(x, rect.bottom())); } diff --git a/DSView/pv/view/trace.cpp b/DSView/pv/view/trace.cpp index c9971bc8..7c923081 100644 --- a/DSView/pv/view/trace.cpp +++ b/DSView/pv/view/trace.cpp @@ -186,7 +186,7 @@ void Trace::set_old_v_offset(int v_offset) _old_v_offset = v_offset; } -int Trace::get_zeroPos() +int Trace::get_zero_vpos() { return _v_offset; } @@ -256,7 +256,7 @@ void Trace::paint_label(QPainter &p, int right, const QPoint pt) const QRectF color_rect = get_rect("color", y, right); const QRectF name_rect = get_rect("name", y, right); - const QRectF label_rect = get_rect("label", get_zeroPos(), right); + const QRectF label_rect = get_rect("label", get_zero_vpos(), right); //p.setRenderHint(QPainter::Antialiasing); // Paint the ColorButton @@ -276,7 +276,7 @@ void Trace::paint_label(QPainter &p, int right, const QPoint pt) const QPointF points[] = { label_rect.topLeft(), label_rect.topRight(), - QPointF(right, get_zeroPos()), + QPointF(right, get_zero_vpos()), label_rect.bottomRight(), label_rect.bottomLeft() }; @@ -339,7 +339,7 @@ int Trace::pt_in_rect(int y, int right, const QPoint &point) { const QRectF color = get_rect("color", y, right); const QRectF name = get_rect("name", y, right); - const QRectF label = get_rect("label", get_zeroPos(), right); + const QRectF label = get_rect("label", get_zero_vpos(), right); if (color.contains(point) && enabled()) return COLOR; @@ -364,10 +364,10 @@ void Trace::compute_text_size(QPainter &p) p.boundingRect(QRectF(), 0, "99").height()); } -QRectF Trace::get_view_rect() const +QRect Trace::get_view_rect() const { assert(_view); - return QRectF(0, 0, _view->viewport()->width(), _view->viewport()->height()); + return QRect(0, 0, _view->viewport()->width(), _view->viewport()->height()); } int Trace::get_y() const diff --git a/DSView/pv/view/trace.h b/DSView/pv/view/trace.h index 2dd9662c..04260258 100644 --- a/DSView/pv/view/trace.h +++ b/DSView/pv/view/trace.h @@ -161,7 +161,7 @@ public: */ void set_old_v_offset(int v_offset); - virtual int get_zeroPos(); + virtual int get_zero_vpos(); /** * Returns true if the trace is visible and enabled. @@ -241,7 +241,7 @@ public: virtual int rows_size(); - virtual QRectF get_view_rect() const; + virtual QRect get_view_rect() const; virtual bool mouse_double_click(int right, const QPoint pt); diff --git a/DSView/pv/view/view.cpp b/DSView/pv/view/view.cpp index 7c9cd6cc..b9d39380 100644 --- a/DSView/pv/view/view.cpp +++ b/DSView/pv/view/view.cpp @@ -155,13 +155,15 @@ View::View(SigSession &session, pv::toolbars::SamplingBar *sampling_bar, QWidget connect(&_session, SIGNAL(receive_header()), this, SLOT(receive_header())); connect(&_session, SIGNAL(receive_trigger(quint64)), - this, SLOT(set_trig_pos(quint64))); + this, SLOT(receive_trigger(quint64))); connect(&_session, SIGNAL(frame_ended()), this, SLOT(receive_end())); connect(&_session, SIGNAL(frame_began()), this, SLOT(frame_began())); connect(&_session, SIGNAL(show_region(uint64_t,uint64_t)), this, SLOT(show_region(uint64_t, uint64_t))); + connect(&_session, SIGNAL(show_wait_trigger()), + _time_viewport, SLOT(show_wait_trigger())); // connect(_devmode, SIGNAL(mode_changed()), // this, SIGNAL(mode_changed())); @@ -248,14 +250,20 @@ void View::update_sample(bool instant) void View::set_sample_rate(uint64_t sample_rate, bool force) { - if (_session.get_capture_state() != pv::SigSession::Stopped || force) + if (_session.get_capture_state() != pv::SigSession::Stopped || force) { _sampling_bar->set_sample_rate(sample_rate); + _session.set_cur_samplerate(_session.get_device()->get_sample_rate()); + } } void View::set_sample_limit(uint64_t sample_limit, bool force) { - if (_session.get_capture_state() != pv::SigSession::Stopped || force) + if (_session.get_capture_state() != pv::SigSession::Stopped || force) { _sampling_bar->set_sample_limit(sample_limit); + const uint64_t final_limit = _session.get_device()->get_sample_limit(); + _trig_cursor->set_index(_trig_cursor->index() * 1.0 / _session.cur_samplelimits() * final_limit); + _session.set_cur_samplelimits(final_limit); + } } void View::zoom(double steps, int offset) @@ -442,13 +450,16 @@ void View::receive_end() } } } + _time_viewport->unshow_wait_trigger(); } -void View::set_trig_pos(quint64 trig_pos) +void View::receive_trigger(quint64 trig_pos) { const double time = trig_pos * 1.0 / _session.cur_samplerate(); _trig_cursor->set_index(trig_pos); - if (ds_trigger_get_en() || _session.get_device()->name() == "virtual-session") { + if (ds_trigger_get_en() || + _session.get_device()->name() == "virtual-session" || + _session.get_device()->dev_inst()->mode == DSO) { _show_trig_cursor = true; set_scale_offset(_scale, time - _scale * get_view_width() / 2); } @@ -457,6 +468,12 @@ void View::set_trig_pos(quint64 trig_pos) 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) { //assert(search_pos >= 0); @@ -474,7 +491,7 @@ uint64_t View::get_search_pos() return _search_pos; } -const QPointF& View::hover_point() const +const QPoint& View::hover_point() const { return _hover_point; } @@ -567,7 +584,7 @@ void View::update_scale_offset() _preScale = _scale; _preOffset = _offset; - _trig_cursor->set_index(_session.get_trigger_pos()); + //_trig_cursor->set_index(_session.get_trigger_pos()); _ruler->update(); viewport_update(); @@ -676,10 +693,10 @@ bool View::eventFilter(QObject *object, QEvent *event) if (object == _ruler || object == _time_viewport || object == _fft_viewport) { //_hover_point = QPoint(mouse_event->x(), 0); double cur_periods = (mouse_event->pos().x() * _scale + _offset) / _ruler->get_min_period(); - double integer_x = (round(cur_periods) * _ruler->get_min_period() - _offset ) / _scale; + int integer_x = (round(cur_periods) * _ruler->get_min_period() - _offset ) / _scale; double cur_deviate_x = qAbs(mouse_event->pos().x() - integer_x); if (cur_deviate_x < 10) - _hover_point = QPointF(integer_x, mouse_event->pos().y()); + _hover_point = QPoint(integer_x, mouse_event->pos().y()); else _hover_point = mouse_event->pos(); } else if (object == _header) @@ -799,6 +816,7 @@ void View::data_updated() update_scroll(); // Repaint the view + _time_viewport->unshow_wait_trigger(); set_update(_time_viewport, true); set_update(_fft_viewport, true); viewport_update(); @@ -953,13 +971,25 @@ void View::on_state_changed(bool stop) update_scale_offset(); } +QRect View::get_view_rect() +{ + if (_session.get_device()->dev_inst()->mode == DSO) { + const vector< boost::shared_ptr > sigs(_session.get_signals()); + BOOST_FOREACH(const boost::shared_ptr s, sigs) { + return s->get_view_rect(); + } + } else { + return _viewcenter->rect(); + } +} + int View::get_view_width() { int view_width = 0; if (_session.get_device()->dev_inst()->mode == DSO) { const vector< boost::shared_ptr > sigs(_session.get_signals()); BOOST_FOREACH(const boost::shared_ptr s, sigs) { - view_width = max((double)view_width, s->get_view_rect().width()); + view_width = max(view_width, s->get_view_rect().width()); } } else { view_width = _viewcenter->width(); @@ -974,7 +1004,7 @@ int View::get_view_height() if (_session.get_device()->dev_inst()->mode == DSO) { const vector< boost::shared_ptr > sigs(_session.get_signals()); BOOST_FOREACH(const boost::shared_ptr s, sigs) { - view_height = max((double)view_height, s->get_view_rect().height()); + view_height = max(view_height, s->get_view_rect().height()); } } else { view_height = _viewcenter->height(); diff --git a/DSView/pv/view/view.h b/DSView/pv/view/view.h index cfc5466b..9c73212e 100644 --- a/DSView/pv/view/view.h +++ b/DSView/pv/view/view.h @@ -139,7 +139,7 @@ public: */ void show_cursors(bool show = true); - const QPointF& hover_point() const; + const QPoint& hover_point() const; void normalize_layout(); @@ -158,7 +158,6 @@ public: Cursor* get_trig_cursor(); Cursor* get_search_cursor(); - //void set_trig_pos(uint64_t trig_pos); void set_search_pos(uint64_t search_pos); uint64_t get_search_pos(); @@ -180,6 +179,7 @@ public: void on_state_changed(bool stop); + QRect get_view_rect(); int get_view_width(); int get_view_height(); @@ -248,7 +248,8 @@ private slots: void receive_header(); - void set_trig_pos(quint64 trig_pos); + void receive_trigger(quint64 trig_pos); + void set_trig_pos(int percent); void receive_end(); @@ -302,7 +303,7 @@ private: bool _show_search_cursor; uint64_t _search_pos; - QPointF _hover_point; + QPoint _hover_point; dialogs::Calibration *_cali; }; diff --git a/DSView/pv/view/viewport.cpp b/DSView/pv/view/viewport.cpp index 38f0c117..9185c5c1 100644 --- a/DSView/pv/view/viewport.cpp +++ b/DSView/pv/view/viewport.cpp @@ -68,7 +68,8 @@ Viewport::Viewport(View &parent, View_type type) : _hover_index(0), _hover_hit(false), _dso_xm_valid(false), - _dso_ym_valid(false) + _dso_ym_valid(false), + _waiting_trig(0) { setMouseTracking(true); setAutoFillBackground(true); @@ -236,6 +237,16 @@ void Viewport::paintSignals(QPainter &p) //plot measure arrow paintMeasure(p); + + //plot waiting trigger + if (_waiting_trig > 0) { + p.setPen(Trace::DARK_FORE); + QString text = "Waiting Trig"; + for (int i = 1; i < _waiting_trig; i++) + if (i % (WaitLoopTime / SigSession::ViewTime) == 0) + text += "."; + p.drawText(_view.get_view_rect(), Qt::AlignLeft | Qt::AlignTop, text); + } } } @@ -446,7 +457,7 @@ void Viewport::mouseMoveEvent(QMouseEvent *event) if (_drag_sig) { boost::shared_ptr dsoSig; if (dsoSig = dynamic_pointer_cast(_drag_sig)) - dsoSig->set_trig_vpos(event->pos().y()); + dsoSig->set_trig_vpos(event->pos().y(), true); } } @@ -454,7 +465,7 @@ void Viewport::mouseMoveEvent(QMouseEvent *event) uint64_t sample_rate = _view.session().cur_samplerate(); TimeMarker* grabbed_marker = _view.get_ruler()->get_grabbed_cursor(); if (_view.cursors_shown() && grabbed_marker) { - double curX = _view.hover_point().x(); + int curX = _view.hover_point().x(); uint64_t index0 = 0, index1 = 0, index2 = 0; bool logic = false; const vector< boost::shared_ptr > sigs(_view.session().get_signals()); @@ -1279,6 +1290,19 @@ void Viewport::set_need_update(bool update) _need_update = update; } +void Viewport::show_wait_trigger() +{ + _waiting_trig %= (WaitLoopTime / SigSession::ViewTime) * 4; + _waiting_trig++; + update(); +} + +void Viewport::unshow_wait_trigger() +{ + _waiting_trig = 0; + update(); +} + } // namespace view } // namespace pv diff --git a/DSView/pv/view/viewport.h b/DSView/pv/view/viewport.h index 9afef47b..588dbda0 100644 --- a/DSView/pv/view/viewport.h +++ b/DSView/pv/view/viewport.h @@ -59,6 +59,7 @@ public: static const double MinorDragRateUp; static const double DragDamping; static const int SnapMinSpace = 10; + static const int WaitLoopTime = 400; enum ActionType { NO_ACTION, @@ -123,6 +124,10 @@ private slots: void on_drag_timer(); void set_receive_len(quint64 length); +public slots: + void show_wait_trigger(); + void unshow_wait_trigger(); + signals: void measure_updated(); @@ -188,6 +193,8 @@ private: uint64_t _dso_ym_index; int _dso_ym_start; int _dso_ym_end; + + int _waiting_trig; }; } // namespace view diff --git a/libsigrok4DSL/hardware/DSL/dscope.c b/libsigrok4DSL/hardware/DSL/dscope.c index 83928db4..b3d1f71c 100644 --- a/libsigrok4DSL/hardware/DSL/dscope.c +++ b/libsigrok4DSL/hardware/DSL/dscope.c @@ -91,6 +91,7 @@ static const int32_t sessions[] = { SR_CONF_TRIGGER_SOURCE, SR_CONF_HORIZ_TRIGGERPOS, SR_CONF_TRIGGER_HOLDOFF, + SR_CONF_TRIGGER_MARGIN, }; static const char *probe_names[] = { @@ -392,7 +393,7 @@ static int fpga_config(struct libusb_device_handle *hdl, const char *filename) struct stat f_stat; sr_info("Configure FPGA using %s", filename); - if ((fw = g_fopen(filename, "rb")) == NULL) { + if ((fw = fopen(filename, "rb")) == NULL) { sr_err("Unable to open FPGA bit file %s for reading: %s", filename, strerror(errno)); return SR_ERR; @@ -636,6 +637,8 @@ static struct DSL_context *DSCope_dev_new(void) devc->zero = FALSE; devc->data_lock = FALSE; devc->cali = FALSE; + devc->dso_bits = 8; + devc->trigger_margin = 0; return devc; } @@ -1023,6 +1026,10 @@ static uint64_t dso_cmd_gen(struct sr_dev_inst *sdi, struct sr_channel* ch, int cmd += probe->trig_value << (8 * (probe->index + 1)); } break; + case SR_CONF_TRIGGER_MARGIN: + cmd += 0x40; + cmd += ((uint64_t)devc->trigger_margin << 8); + break; case SR_CONF_TRIGGER_HOLDOFF: cmd += 0x58; cmd += ((uint64_t)devc->trigger_holdoff << 8); @@ -1237,6 +1244,12 @@ static int config_get(int id, GVariant **data, const struct sr_dev_inst *sdi, devc = sdi->priv; *data = g_variant_new_uint64(devc->trigger_holdoff); break; + case SR_CONF_TRIGGER_MARGIN: + if (!sdi) + return SR_ERR; + devc = sdi->priv; + *data = g_variant_new_byte(devc->trigger_margin); + break; case SR_CONF_ZERO: if (!sdi) return SR_ERR; @@ -1335,6 +1348,12 @@ static int config_get(int id, GVariant **data, const struct sr_dev_inst *sdi, return SR_ERR; *data = g_variant_new_uint16(CALI_VOFF_RANGE); break; + case SR_CONF_DSO_BITS: + if (!sdi) + return SR_ERR; + devc = sdi->priv; + *data = g_variant_new_byte(devc->dso_bits); + break; default: return SR_ERR_NA; } @@ -1600,6 +1619,17 @@ static int config_set(int id, GVariant *data, struct sr_dev_inst *sdi, else sr_dbg("%s: setting Trigger Holdoff Time to %d failed", __func__, devc->trigger_holdoff); + } else if (id == SR_CONF_TRIGGER_MARGIN) { + devc->trigger_margin = g_variant_get_byte(data); + if (sdi->mode == DSO) { + ret = command_dso_ctrl(usb->devhdl, dso_cmd_gen(sdi, NULL, SR_CONF_TRIGGER_MARGIN)); + } + if (ret == SR_OK) + sr_dbg("%s: setting Trigger Margin to %d", + __func__, devc->trigger_margin); + else + sr_dbg("%s: setting Trigger Margin to %d failed", + __func__, devc->trigger_margin); } else if (id == SR_CONF_ZERO) { devc->zero = g_variant_get_boolean(data); if (devc->zero) { @@ -1873,6 +1903,11 @@ static int dso_init(struct sr_dev_inst *sdi, gboolean from_eep) sr_err("Set Trigger Value command failed!"); return ret; } + ret = command_dso_ctrl(usb->devhdl, dso_cmd_gen(sdi, NULL, SR_CONF_TRIGGER_MARGIN)); + if (ret != SR_OK) { + sr_err("Set Trigger Margin command failed!"); + return ret; + } return ret; } diff --git a/libsigrok4DSL/hardware/DSL/dsl.h b/libsigrok4DSL/hardware/DSL/dsl.h index 6fd59f00..dcc94f2a 100644 --- a/libsigrok4DSL/hardware/DSL/dsl.h +++ b/libsigrok4DSL/hardware/DSL/dsl.h @@ -224,6 +224,7 @@ struct DSL_context { uint8_t trigger_hrate; uint32_t trigger_hpos; uint32_t trigger_holdoff; + uint8_t trigger_margin; gboolean zero; gboolean cali; int zero_stage; @@ -231,6 +232,7 @@ struct DSL_context { int zero_comb; gboolean stream; gboolean data_lock; + uint8_t dso_bits; int num_samples; uint64_t sent_samples; diff --git a/libsigrok4DSL/hardware/DSL/dslogic.c b/libsigrok4DSL/hardware/DSL/dslogic.c index 842ed953..3cf3a7e8 100644 --- a/libsigrok4DSL/hardware/DSL/dslogic.c +++ b/libsigrok4DSL/hardware/DSL/dslogic.c @@ -146,6 +146,7 @@ static const int32_t sessions[] = { SR_CONF_TRIGGER_SOURCE, SR_CONF_HORIZ_TRIGGERPOS, SR_CONF_TRIGGER_HOLDOFF, + SR_CONF_TRIGGER_MARGIN, }; static const int32_t sessions_pro[] = { @@ -161,6 +162,7 @@ static const int32_t sessions_pro[] = { SR_CONF_TRIGGER_SOURCE, SR_CONF_HORIZ_TRIGGERPOS, SR_CONF_TRIGGER_HOLDOFF, + SR_CONF_TRIGGER_MARGIN, }; static const int32_t ch_sessions[] = { @@ -457,7 +459,7 @@ static int fpga_config(struct libusb_device_handle *hdl, const char *filename) struct stat f_stat; sr_info("Configure FPGA using %s", filename); - if ((fw = g_fopen(filename, "rb")) == NULL) { + if ((fw = fopen(filename, "rb")) == NULL) { sr_err("Unable to open FPGA bit file %s for reading: %s", filename, strerror(errno)); return SR_ERR; @@ -701,6 +703,7 @@ static struct DSL_context *DSLogic_dev_new(void) devc->mstatus_valid = FALSE; devc->data_lock = FALSE; devc->max_height = 0; + devc->dso_bits = 8; return devc; } @@ -999,6 +1002,10 @@ static uint64_t dso_cmd_gen(struct sr_dev_inst *sdi, struct sr_channel* ch, int cmd += probe->trig_value << (8 * (probe->index + 1)); } break; + case SR_CONF_TRIGGER_MARGIN: + cmd += 0x40; + cmd += ((uint64_t)devc->trigger_margin << 8); + break; case SR_CONF_TRIGGER_HOLDOFF: cmd += 0x58; cmd += devc->trigger_holdoff << 8; @@ -1013,6 +1020,63 @@ static uint64_t dso_cmd_gen(struct sr_dev_inst *sdi, struct sr_channel* ch, int return cmd; } +static int dso_init(struct sr_dev_inst *sdi) +{ + int ret; + GSList *l; + struct sr_usb_dev_inst *usb = sdi->conn; + + for(l = sdi->channels; l; l = l->next) { + struct sr_channel *probe = (struct sr_channel *)l->data; + ret = command_dso_ctrl(usb->devhdl, dso_cmd_gen(sdi, probe, SR_CONF_COUPLING)); + if (ret != SR_OK) { + sr_err("DSO set coupling of channel %d command failed!", probe->index); + return ret; + } + ret = command_dso_ctrl(usb->devhdl, dso_cmd_gen(sdi, probe, SR_CONF_VDIV)); + if (ret != SR_OK) { + sr_err("Set VDIV of channel %d command failed!", probe->index); + return ret; + } + } + + ret = command_dso_ctrl(usb->devhdl, dso_cmd_gen(sdi, 0, SR_CONF_SAMPLERATE)); + if (ret != SR_OK) { + sr_err("Set Sample Rate command failed!"); + return ret; + } + ret = command_dso_ctrl(usb->devhdl, dso_cmd_gen(sdi, NULL, SR_CONF_HORIZ_TRIGGERPOS)); + if (ret != SR_OK) { + sr_err("Set Horiz Trigger Position command failed!"); + return ret; + } + ret = command_dso_ctrl(usb->devhdl, dso_cmd_gen(sdi, NULL, SR_CONF_TRIGGER_HOLDOFF)); + if (ret != SR_OK) { + sr_err("Set Trigger Holdoff Time command failed!"); + return ret; + } + ret = command_dso_ctrl(usb->devhdl, dso_cmd_gen(sdi, NULL, SR_CONF_TRIGGER_SLOPE)); + if (ret != SR_OK) { + sr_err("Set Trigger Slope command failed!"); + return ret; + } + ret = command_dso_ctrl(usb->devhdl, dso_cmd_gen(sdi, NULL, SR_CONF_TRIGGER_SOURCE)); + if (ret != SR_OK) { + sr_err("Set Trigger Source command failed!"); + return ret; + } + ret = command_dso_ctrl(usb->devhdl, dso_cmd_gen(sdi, NULL, SR_CONF_TRIGGER_VALUE)); + if (ret != SR_OK) { + sr_err("Set Trigger Value command failed!"); + return ret; + } + ret = command_dso_ctrl(usb->devhdl, dso_cmd_gen(sdi, NULL, SR_CONF_TRIGGER_MARGIN)); + if (ret != SR_OK) { + sr_err("Set Trigger Margin command failed!"); + return ret; + } + return ret; +} static int config_get(int id, GVariant **data, const struct sr_dev_inst *sdi, const struct sr_channel *ch, @@ -1200,6 +1264,12 @@ static int config_get(int id, GVariant **data, const struct sr_dev_inst *sdi, devc = sdi->priv; *data = g_variant_new_uint64(devc->trigger_holdoff); break; + case SR_CONF_TRIGGER_MARGIN: + if (!sdi) + return SR_ERR; + devc = sdi->priv; + *data = g_variant_new_byte(devc->trigger_margin); + break; case SR_CONF_ZERO: if (!sdi) return SR_ERR; @@ -1242,6 +1312,12 @@ static int config_get(int id, GVariant **data, const struct sr_dev_inst *sdi, devc = sdi->priv; *data = g_variant_new_uint64(DSLOGIC_MAX_LOGIC_DEPTH*ceil(samplerates[devc->samplerates_size-1] * 1.0 / DSLOGIC_MAX_LOGIC_SAMPLERATE)); break; + case SR_CONF_DSO_BITS: + if (!sdi) + return SR_ERR; + devc = sdi->priv; + *data = g_variant_new_byte(devc->dso_bits); + break; default: return SR_ERR_NA; } @@ -1325,51 +1401,7 @@ static int config_set(int id, GVariant *data, struct sr_dev_inst *sdi, set_probes(sdi, num_probes); sr_dbg("%s: setting mode to %d", __func__, sdi->mode); if (sdi->mode == DSO) { - GList *l; - for(l = sdi->channels; l; l = l->next) { - struct sr_channel *probe = (struct sr_channel *)l->data; - ret = command_dso_ctrl(usb->devhdl, dso_cmd_gen(sdi, probe, SR_CONF_COUPLING)); - if (ret != SR_OK) { - sr_err("Set Coupling command failed!"); - return ret; - } - ret = command_dso_ctrl(usb->devhdl, dso_cmd_gen(sdi, probe, SR_CONF_VDIV)); - if (ret != SR_OK) { - sr_dbg("%s: Initial setting for DSO mode failed", __func__); - return ret; - } - } - - ret = command_dso_ctrl(usb->devhdl, dso_cmd_gen(sdi, 0, SR_CONF_SAMPLERATE)); - if (ret != SR_OK) { - sr_err("Set Sample Rate command failed!"); - return ret; - } - ret = command_dso_ctrl(usb->devhdl, dso_cmd_gen(sdi, NULL, SR_CONF_HORIZ_TRIGGERPOS)); - if (ret != SR_OK) { - sr_err("Set Horiz Trigger Position command failed!"); - return ret; - } - ret = command_dso_ctrl(usb->devhdl, dso_cmd_gen(sdi, NULL, SR_CONF_TRIGGER_HOLDOFF)); - if (ret != SR_OK) { - sr_err("Set Trigger Holdoff Time command failed!"); - return ret; - } - ret = command_dso_ctrl(usb->devhdl, dso_cmd_gen(sdi, NULL, SR_CONF_TRIGGER_SLOPE)); - if (ret != SR_OK) { - sr_err("Set Trigger Slope command failed!"); - return ret; - } - ret = command_dso_ctrl(usb->devhdl, dso_cmd_gen(sdi, NULL, SR_CONF_TRIGGER_SOURCE)); - if (ret != SR_OK) { - sr_err("Set Trigger Source command failed!"); - return ret; - } - ret = command_dso_ctrl(usb->devhdl, dso_cmd_gen(sdi, NULL, SR_CONF_TRIGGER_VALUE)); - if (ret != SR_OK) { - sr_err("Set Trigger Value command failed!"); - return ret; - } + dso_init(sdi); } } else if (id == SR_CONF_OPERATION_MODE) { stropt = g_variant_get_string(data, NULL); @@ -1560,6 +1592,13 @@ static int config_set(int id, GVariant *data, struct sr_dev_inst *sdi, __func__, ch->index, ch->vdiv); } else if (id == SR_CONF_FACTOR) { ch->vfactor = g_variant_get_uint64(data); + sr_dbg("%s: setting Factor of channel %d to %d", __func__, + ch->index, ch->vfactor); + ret = SR_OK; + } else if (id == SR_CONF_VPOS) { + ch->vpos = g_variant_get_double(data); + sr_dbg("%s: setting VPOS of channel %d to %lf", __func__, + ch->index, ch->vpos); ret = SR_OK; } else if (id == SR_CONF_TIMEBASE) { devc->timebase = g_variant_get_uint64(data); @@ -1643,6 +1682,17 @@ static int config_set(int id, GVariant *data, struct sr_dev_inst *sdi, else sr_dbg("%s: setting Trigger Holdoff Time to %d failed", __func__, devc->trigger_holdoff); + } else if (id == SR_CONF_TRIGGER_MARGIN) { + devc->trigger_margin = g_variant_get_byte(data); + if (sdi->mode == DSO) { + ret = command_dso_ctrl(usb->devhdl, dso_cmd_gen(sdi, NULL, SR_CONF_TRIGGER_MARGIN)); + } + if (ret == SR_OK) + sr_dbg("%s: setting Trigger Margin to %d", + __func__, devc->trigger_margin); + else + sr_dbg("%s: setting Trigger Margin to %d failed", + __func__, devc->trigger_margin); } else if (id == SR_CONF_ZERO) { devc->zero = g_variant_get_boolean(data); ret = SR_OK; diff --git a/libsigrok4DSL/hardware/demo/demo.c b/libsigrok4DSL/hardware/demo/demo.c index 60e62a8c..0cd35c31 100644 --- a/libsigrok4DSL/hardware/demo/demo.c +++ b/libsigrok4DSL/hardware/demo/demo.c @@ -110,6 +110,7 @@ struct dev_context { gboolean instant; gboolean data_lock; uint8_t max_height; + uint8_t dso_bits; uint16_t *buf; uint64_t pre_index; @@ -345,6 +346,7 @@ static GSList *hw_scan(GSList *options) devc->timebase = 200; devc->data_lock = FALSE; devc->max_height = 0; + devc->dso_bits = 8; sdi->priv = devc; @@ -530,6 +532,9 @@ static int config_get(int id, GVariant **data, const struct sr_dev_inst *sdi, case SR_CONF_RLE_SAMPLELIMITS: *data = g_variant_new_uint64(DEMO_MAX_LOGIC_DEPTH); break; + case SR_CONF_DSO_BITS: + *data = g_variant_new_byte(devc->dso_bits); + break; default: return SR_ERR_NA; } @@ -655,6 +660,8 @@ static int config_set(int id, GVariant *data, struct sr_dev_inst *sdi, ret = SR_OK; } else if (id == SR_CONF_TRIGGER_HOLDOFF) { ret = SR_OK; + } else if (id == SR_CONF_TRIGGER_MARGIN) { + ret = SR_OK; } else if (id == SR_CONF_EN_CH) { ch->enabled = g_variant_get_boolean(data); sr_dbg("%s: setting ENABLE of channel %d to %d", __func__, @@ -678,7 +685,7 @@ static int config_set(int id, GVariant *data, struct sr_dev_inst *sdi, ch->index, ch->vfactor); ret = SR_OK; } else if (id == SR_CONF_VPOS) { - ch->vpos = g_variant_get_double(data); + //ch->vpos = g_variant_get_double(data); sr_dbg("%s: setting VPOS of channel %d to %lf", __func__, ch->index, ch->vpos); ret = SR_OK; diff --git a/libsigrok4DSL/hwdriver.c b/libsigrok4DSL/hwdriver.c index da3bcd35..e8917a84 100644 --- a/libsigrok4DSL/hwdriver.c +++ b/libsigrok4DSL/hwdriver.c @@ -80,6 +80,8 @@ static struct sr_config_info sr_config_info_data[] = { "Horizontal trigger position", "Horizontal trigger position", NULL}, {SR_CONF_TRIGGER_HOLDOFF, SR_T_UINT64, "triggerholdoff", "Trigger hold off", "Trigger hold off", NULL}, + {SR_CONF_TRIGGER_MARGIN, SR_T_UINT8, "triggermargin", + "Trigger margin", "Trigger margin", NULL}, {SR_CONF_BUFFERSIZE, SR_T_UINT64, "buffersize", "Buffer size", "Buffer size", NULL}, {SR_CONF_TIMEBASE, SR_T_UINT64, "timebase", diff --git a/libsigrok4DSL/libsigrok.h b/libsigrok4DSL/libsigrok.h index 72a022d9..f964940a 100644 --- a/libsigrok4DSL/libsigrok.h +++ b/libsigrok4DSL/libsigrok.h @@ -727,6 +727,9 @@ enum { /** Trigger hold off time */ SR_CONF_TRIGGER_HOLDOFF, + /** Trigger Margin */ + SR_CONF_TRIGGER_MARGIN, + /** Buffer size. */ SR_CONF_BUFFERSIZE, @@ -739,6 +742,9 @@ enum { /** DSO configure sync */ SR_CONF_DSO_SYNC, + /** DSO vertical resolution*/ + SR_CONF_DSO_BITS, + /** Zero */ SR_CONF_ZERO_SET, SR_CONF_ZERO_LOAD, From f15aa50d32e51ee67dace9efe0f0044c2876b276 Mon Sep 17 00:00:00 2001 From: DreamSourceLab Date: Wed, 20 Jul 2016 08:59:39 +0800 Subject: [PATCH 28/32] Improve usb transfer and othre minor issues --- DSView/CMakeLists.txt | 232 +- DSView/darkstyle/style.qss | 2 +- DSView/main.cpp | 2 +- DSView/pv/data/decoderstack.cpp | 10 +- DSView/pv/data/decoderstack.h | 6 +- DSView/pv/data/dsosnapshot.cpp | 8 +- DSView/pv/data/mathstack.cpp | 4 +- DSView/pv/device/device.cpp | 12 +- DSView/pv/dialogs/about.ui | 69 - DSView/pv/dialogs/calibration.cpp | 29 +- DSView/pv/dialogs/dsdialog.cpp | 1 + DSView/pv/dialogs/dsmessagebox.cpp | 1 + DSView/pv/dialogs/dsomeasure.cpp | 2 +- DSView/pv/dialogs/fftoptions.cpp | 10 +- DSView/pv/dialogs/messagebox.cpp | 86 - DSView/pv/dialogs/protocolexp.cpp | 2 +- DSView/pv/dialogs/waitingdialog.cpp | 12 +- DSView/pv/dialogs/waitingdialog.h | 2 +- DSView/pv/dock/dsotriggerdock.cpp | 86 + DSView/pv/dock/dsotriggerdock.h | 2 + DSView/pv/dock/measuredock.cpp | 6 +- DSView/pv/dock/protocoldock.cpp | 8 +- DSView/pv/dock/searchdock.cpp | 44 +- DSView/pv/dock/searchdock.h | 1 + DSView/pv/mainwindow.cpp | 80 +- DSView/pv/prop/enum.cpp | 3 + DSView/pv/sigsession.cpp | 61 +- DSView/pv/sigsession.h | 13 +- DSView/pv/toolbars/filebar.cpp | 7 +- DSView/pv/toolbars/filebar.h | 2 + DSView/pv/toolbars/samplingbar.cpp | 33 +- DSView/pv/toolbars/titlebar.cpp | 4 +- DSView/pv/view/decodetrace.cpp | 6 +- DSView/pv/view/devmode.cpp | 3 + DSView/pv/view/dsldial.cpp | 3 +- DSView/pv/view/dsosignal.cpp | 87 +- DSView/pv/view/dsosignal.h | 14 +- DSView/pv/view/header.cpp | 2 +- DSView/pv/view/mathtrace.cpp | 19 +- DSView/pv/view/view.cpp | 8 +- DSView/pv/view/view.h | 3 +- DSView/pv/view/viewport.cpp | 50 +- DSView/pv/widgets/border.cpp | 4 +- DSView/res/DSCope.bin | Bin 340884 -> 341160 bytes DSView/res/DSCope.fw | Bin 8120 -> 8120 bytes DSView/res/DSCope1.def.dsc | 6 +- DSView/res/DSCope1.dsc | 40 - DSView/res/DSCope20.bin | Bin 0 -> 341160 bytes DSView/res/DSCope20.fw | Bin 0 -> 8120 bytes DSView/res/DSLogic.fw | Bin 8120 -> 8120 bytes DSView/res/DSLogic0.def.dsc | 538 ++-- DSView/res/DSLogic0.dsc | 268 -- DSView/res/DSLogic1.def.dsc | 10 +- DSView/res/DSLogic1.dsc | 43 - DSView/res/DSLogic2.def.dsc | 18 +- DSView/res/DSLogic2.dsc | 89 - DSView/res/DSLogic33.bin | Bin 340884 -> 340884 bytes DSView/res/DSLogic50.bin | Bin 340884 -> 340884 bytes DSView/res/DSLogicPro.bin | Bin 340884 -> 340884 bytes DSView/res/DSLogicPro.fw | Bin 8120 -> 8120 bytes INSTALL | 10 +- libsigrok4DSL/hardware/DSL/command.c | 19 + libsigrok4DSL/hardware/DSL/command.h | 3 + libsigrok4DSL/hardware/DSL/dscope.c | 736 +++--- libsigrok4DSL/hardware/DSL/dsl.h | 21 +- libsigrok4DSL/hardware/DSL/dslogic.c | 736 +++--- libsigrok4DSL/hardware/demo/demo.c | 56 +- libsigrok4DSL/hwdriver.c | 2 + libsigrok4DSL/libsigrok.h | 25 +- libsigrokdecode4DSL/.decoder.c.swp | Bin 0 -> 16384 bytes libsigrokdecode4DSL/.gitignore | 38 + libsigrokdecode4DSL/.type_decoder.c.swp | Bin 0 -> 16384 bytes libsigrokdecode4DSL/AUTHORS | 7 + libsigrokdecode4DSL/COPYING | 674 +++++ libsigrokdecode4DSL/Doxyfile | 2318 +++++++++++++++++ libsigrokdecode4DSL/HACKING | 200 ++ libsigrokdecode4DSL/Makefile.am | 96 + libsigrokdecode4DSL/NEWS | 223 ++ libsigrokdecode4DSL/README | 101 + libsigrokdecode4DSL/autogen.sh | 24 + libsigrokdecode4DSL/configure.ac | 161 ++ .../contrib/sigrok-logo-notext.png | Bin 0 -> 8637 bytes libsigrokdecode4DSL/decoder.c | 856 ++++++ .../decoders/0-i2c/__init__.py | 26 + libsigrokdecode4DSL/decoders/0-i2c/pd.py | 343 +++ libsigrokdecode4DSL/decoders/0-spi/.pd.py.swo | Bin 0 -> 16384 bytes libsigrokdecode4DSL/decoders/0-spi/.pd.py.swp | Bin 0 -> 24576 bytes .../decoders/0-spi/__init__.py | 32 + libsigrokdecode4DSL/decoders/0-spi/pd.py | 293 +++ .../decoders/0-uart/.pd.py.swo | Bin 0 -> 32768 bytes .../decoders/0-uart/.pd.py.swp | Bin 0 -> 40960 bytes .../decoders/0-uart/__init__.py | 41 + libsigrokdecode4DSL/decoders/0-uart/pd.py | 311 +++ libsigrokdecode4DSL/decoders/1-i2c/.pd.py.swo | Bin 0 -> 28672 bytes libsigrokdecode4DSL/decoders/1-i2c/.pd.py.swp | Bin 0 -> 36864 bytes .../decoders/1-i2c/__init__.py | 26 + libsigrokdecode4DSL/decoders/1-i2c/pd.py | 341 +++ libsigrokdecode4DSL/decoders/1-spi/.pd.py.swn | Bin 0 -> 24576 bytes libsigrokdecode4DSL/decoders/1-spi/.pd.py.swo | Bin 0 -> 16384 bytes libsigrokdecode4DSL/decoders/1-spi/.pd.py.swp | Bin 0 -> 24576 bytes .../decoders/1-spi/__init__.py | 32 + libsigrokdecode4DSL/decoders/1-spi/pd.py | 294 +++ .../decoders/1-uart/.pd.py.swo | Bin 0 -> 32768 bytes .../decoders/1-uart/.pd.py.swp | Bin 0 -> 40960 bytes .../decoders/1-uart/__init__.py | 41 + libsigrokdecode4DSL/decoders/1-uart/pd.py | 311 +++ .../decoders/adns5020/__init__.py | 26 + libsigrokdecode4DSL/decoders/adns5020/pd.py | 113 + .../decoders/am230x/__init__.py | 37 + libsigrokdecode4DSL/decoders/am230x/pd.py | 236 ++ .../decoders/arm_etmv3/__init__.py | 26 + libsigrokdecode4DSL/decoders/arm_etmv3/pd.py | 564 ++++ .../decoders/arm_itm/__init__.py | 26 + libsigrokdecode4DSL/decoders/arm_itm/pd.py | 370 +++ .../decoders/arm_tpiu/__init__.py | 28 + libsigrokdecode4DSL/decoders/arm_tpiu/pd.py | 128 + libsigrokdecode4DSL/decoders/aud/__init__.py | 32 + libsigrokdecode4DSL/decoders/aud/pd.py | 116 + .../decoders/avr_isp/__init__.py | 26 + libsigrokdecode4DSL/decoders/avr_isp/parts.py | 42 + libsigrokdecode4DSL/decoders/avr_isp/pd.py | 210 ++ libsigrokdecode4DSL/decoders/can/__init__.py | 30 + libsigrokdecode4DSL/decoders/can/pd.py | 385 +++ .../decoders/dcf77/__init__.py | 29 + libsigrokdecode4DSL/decoders/dcf77/pd.py | 316 +++ .../decoders/ds1307/__init__.py | 26 + libsigrokdecode4DSL/decoders/ds1307/pd.py | 264 ++ libsigrokdecode4DSL/decoders/edid/__init__.py | 39 + libsigrokdecode4DSL/decoders/edid/config | 1 + libsigrokdecode4DSL/decoders/edid/pd.py | 493 ++++ libsigrokdecode4DSL/decoders/edid/pnpids.txt | 2135 +++++++++++++++ .../decoders/eeprom24xx/__init__.py | 26 + .../decoders/eeprom24xx/lists.py | 171 ++ libsigrokdecode4DSL/decoders/eeprom24xx/pd.py | 430 +++ .../decoders/em4100/__init__.py | 25 + libsigrokdecode4DSL/decoders/em4100/pd.py | 238 ++ .../decoders/guess_bitrate/__init__.py | 37 + .../decoders/guess_bitrate/pd.py | 83 + .../decoders/i2cdemux/__init__.py | 28 + libsigrokdecode4DSL/decoders/i2cdemux/pd.py | 77 + .../decoders/i2cfilter/__init__.py | 37 + libsigrokdecode4DSL/decoders/i2cfilter/pd.py | 89 + libsigrokdecode4DSL/decoders/i2s/__init__.py | 30 + libsigrokdecode4DSL/decoders/i2s/pd.py | 188 ++ .../decoders/ir_nec/__init__.py | 25 + libsigrokdecode4DSL/decoders/ir_nec/lists.py | 51 + libsigrokdecode4DSL/decoders/ir_nec/pd.py | 207 ++ .../decoders/ir_rc5/__init__.py | 25 + libsigrokdecode4DSL/decoders/ir_rc5/lists.py | 94 + libsigrokdecode4DSL/decoders/ir_rc5/pd.py | 184 ++ .../decoders/jitter/__init__.py | 29 + libsigrokdecode4DSL/decoders/jitter/pd.py | 204 ++ libsigrokdecode4DSL/decoders/jtag/__init__.py | 31 + libsigrokdecode4DSL/decoders/jtag/pd.py | 257 ++ .../decoders/jtag_stm32/__init__.py | 30 + libsigrokdecode4DSL/decoders/jtag_stm32/pd.py | 266 ++ libsigrokdecode4DSL/decoders/lm75/__init__.py | 26 + libsigrokdecode4DSL/decoders/lm75/pd.py | 183 ++ libsigrokdecode4DSL/decoders/lpc/__init__.py | 26 + libsigrokdecode4DSL/decoders/lpc/pd.py | 368 +++ .../decoders/max7219/__init__.py | 26 + libsigrokdecode4DSL/decoders/max7219/pd.py | 109 + .../decoders/maxim_ds28ea00/__init__.py | 26 + .../decoders/maxim_ds28ea00/pd.py | 90 + libsigrokdecode4DSL/decoders/mdio/__init__.py | 39 + libsigrokdecode4DSL/decoders/mdio/pd.py | 331 +++ libsigrokdecode4DSL/decoders/midi/__init__.py | 29 + libsigrokdecode4DSL/decoders/midi/lists.py | 469 ++++ libsigrokdecode4DSL/decoders/midi/pd.py | 211 ++ .../decoders/mlx90614/__init__.py | 26 + libsigrokdecode4DSL/decoders/mlx90614/pd.py | 75 + .../decoders/modbus/__init__.py | 29 + libsigrokdecode4DSL/decoders/modbus/pd.py | 931 +++++++ .../decoders/mrf24j40/__init__.py | 26 + .../decoders/mrf24j40/lists.py | 166 ++ libsigrokdecode4DSL/decoders/mrf24j40/pd.py | 134 + .../decoders/mxc6225xu/__init__.py | 29 + libsigrokdecode4DSL/decoders/mxc6225xu/pd.py | 214 ++ .../decoders/nrf24l01/__init__.py | 30 + libsigrokdecode4DSL/decoders/nrf24l01/pd.py | 329 +++ .../decoders/nunchuk/__init__.py | 31 + libsigrokdecode4DSL/decoders/nunchuk/pd.py | 204 ++ .../decoders/onewire_link/__init__.py | 68 + .../decoders/onewire_link/pd.py | 289 ++ .../decoders/onewire_link/pd.py.back | 289 ++ .../decoders/onewire_network/__init__.py | 57 + .../decoders/onewire_network/pd.py | 180 ++ .../decoders/pan1321/__init__.py | 26 + libsigrokdecode4DSL/decoders/pan1321/pd.py | 161 ++ .../decoders/parallel/__init__.py | 35 + libsigrokdecode4DSL/decoders/parallel/pd.py | 192 ++ libsigrokdecode4DSL/decoders/pwm/__init__.py | 25 + libsigrokdecode4DSL/decoders/pwm/pd.py | 151 ++ libsigrokdecode4DSL/decoders/qi/__init__.py | 26 + libsigrokdecode4DSL/decoders/qi/pd.py | 245 ++ .../decoders/rfm12/__init__.py | 26 + libsigrokdecode4DSL/decoders/rfm12/pd.py | 494 ++++ .../decoders/rgb_led_spi/__init__.py | 26 + .../decoders/rgb_led_spi/pd.py | 67 + .../decoders/rgb_led_ws281x/__init__.py | 28 + .../decoders/rgb_led_ws281x/pd.py | 129 + .../decoders/rtc8564/__init__.py | 26 + libsigrokdecode4DSL/decoders/rtc8564/pd.py | 254 ++ .../decoders/sdcard_sd/__init__.py | 25 + .../decoders/sdcard_sd/lists.py | 185 ++ libsigrokdecode4DSL/decoders/sdcard_sd/pd.py | 444 ++++ .../decoders/sdcard_spi/__init__.py | 69 + libsigrokdecode4DSL/decoders/sdcard_spi/pd.py | 440 ++++ .../decoders/spdif/__init__.py | 26 + libsigrokdecode4DSL/decoders/spdif/pd.py | 258 ++ libsigrokdecode4DSL/decoders/spi/__init__.py | 32 + libsigrokdecode4DSL/decoders/spi/pd.py | 334 +++ .../decoders/spiflash/__init__.py | 31 + .../decoders/spiflash/lists.py | 90 + libsigrokdecode4DSL/decoders/spiflash/pd.py | 379 +++ .../decoders/stepper_motor/__init__.py | 26 + .../decoders/stepper_motor/pd.py | 98 + libsigrokdecode4DSL/decoders/swd/__init__.py | 35 + libsigrokdecode4DSL/decoders/swd/pd.py | 350 +++ .../decoders/t55xx/__init__.py | 26 + libsigrokdecode4DSL/decoders/t55xx/pd.py | 332 +++ .../decoders/tca6408a/__init__.py | 26 + libsigrokdecode4DSL/decoders/tca6408a/pd.py | 129 + .../decoders/timing/__init__.py | 25 + libsigrokdecode4DSL/decoders/timing/pd.py | 95 + .../decoders/tlc5620/__init__.py | 25 + libsigrokdecode4DSL/decoders/tlc5620/pd.py | 210 ++ .../decoders/usb_packet/__init__.py | 44 + libsigrokdecode4DSL/decoders/usb_packet/pd.py | 394 +++ .../decoders/usb_power_delivery/__init__.py | 25 + .../decoders/usb_power_delivery/pd.py | 582 +++++ .../decoders/usb_request/__init__.py | 50 + .../decoders/usb_request/pd.py | 359 +++ .../decoders/usb_signalling/__init__.py | 51 + .../decoders/usb_signalling/pd.py | 346 +++ .../decoders/wiegand/__init__.py | 29 + libsigrokdecode4DSL/decoders/wiegand/pd.py | 135 + libsigrokdecode4DSL/decoders/xfp/__init__.py | 40 + libsigrokdecode4DSL/decoders/xfp/pd.py | 646 +++++ libsigrokdecode4DSL/decoders/z80/__init__.py | 35 + libsigrokdecode4DSL/decoders/z80/pd.py | 354 +++ libsigrokdecode4DSL/decoders/z80/tables.py | 1083 ++++++++ libsigrokdecode4DSL/error.c | 153 ++ libsigrokdecode4DSL/exception.c | 92 + libsigrokdecode4DSL/instance.c | 665 +++++ .../libsigrokdecode-internal.h | 96 + libsigrokdecode4DSL/libsigrokdecode.h | 351 +++ libsigrokdecode4DSL/log.c | 185 ++ libsigrokdecode4DSL/m4/sigrok.m4 | 449 ++++ libsigrokdecode4DSL/module_sigrokdecode.c | 88 + libsigrokdecode4DSL/session.c | 344 +++ libsigrokdecode4DSL/srd.c | 261 ++ libsigrokdecode4DSL/tests/core.c | 105 + libsigrokdecode4DSL/tests/decoder.c | 346 +++ libsigrokdecode4DSL/tests/inst.c | 164 ++ .../tests/lib.h | 49 +- libsigrokdecode4DSL/tests/main.c | 57 + libsigrokdecode4DSL/tests/session.c | 251 ++ libsigrokdecode4DSL/tools/install-decoders | 112 + libsigrokdecode4DSL/type_decoder.c | 412 +++ libsigrokdecode4DSL/type_logic.c | 116 + libsigrokdecode4DSL/util.c | 204 ++ libsigrokdecode4DSL/version.c | 149 ++ libsigrokdecode4DSL/version.h.in | 70 + 264 files changed, 36467 insertions(+), 2074 deletions(-) delete mode 100644 DSView/pv/dialogs/about.ui delete mode 100644 DSView/pv/dialogs/messagebox.cpp mode change 100644 => 100755 DSView/res/DSCope.bin mode change 100644 => 100755 DSView/res/DSCope.fw mode change 100644 => 100755 DSView/res/DSCope1.def.dsc delete mode 100644 DSView/res/DSCope1.dsc create mode 100755 DSView/res/DSCope20.bin create mode 100755 DSView/res/DSCope20.fw mode change 100644 => 100755 DSView/res/DSLogic.fw mode change 100644 => 100755 DSView/res/DSLogic0.def.dsc delete mode 100644 DSView/res/DSLogic0.dsc mode change 100644 => 100755 DSView/res/DSLogic1.def.dsc delete mode 100644 DSView/res/DSLogic1.dsc mode change 100644 => 100755 DSView/res/DSLogic2.def.dsc delete mode 100644 DSView/res/DSLogic2.dsc mode change 100644 => 100755 DSView/res/DSLogic33.bin mode change 100644 => 100755 DSView/res/DSLogic50.bin mode change 100644 => 100755 DSView/res/DSLogicPro.bin mode change 100644 => 100755 DSView/res/DSLogicPro.fw create mode 100644 libsigrokdecode4DSL/.decoder.c.swp create mode 100644 libsigrokdecode4DSL/.gitignore create mode 100644 libsigrokdecode4DSL/.type_decoder.c.swp create mode 100644 libsigrokdecode4DSL/AUTHORS create mode 100644 libsigrokdecode4DSL/COPYING create mode 100644 libsigrokdecode4DSL/Doxyfile create mode 100644 libsigrokdecode4DSL/HACKING create mode 100644 libsigrokdecode4DSL/Makefile.am create mode 100644 libsigrokdecode4DSL/NEWS create mode 100644 libsigrokdecode4DSL/README create mode 100755 libsigrokdecode4DSL/autogen.sh create mode 100644 libsigrokdecode4DSL/configure.ac create mode 100644 libsigrokdecode4DSL/contrib/sigrok-logo-notext.png create mode 100644 libsigrokdecode4DSL/decoder.c create mode 100755 libsigrokdecode4DSL/decoders/0-i2c/__init__.py create mode 100755 libsigrokdecode4DSL/decoders/0-i2c/pd.py create mode 100755 libsigrokdecode4DSL/decoders/0-spi/.pd.py.swo create mode 100755 libsigrokdecode4DSL/decoders/0-spi/.pd.py.swp create mode 100755 libsigrokdecode4DSL/decoders/0-spi/__init__.py create mode 100755 libsigrokdecode4DSL/decoders/0-spi/pd.py create mode 100755 libsigrokdecode4DSL/decoders/0-uart/.pd.py.swo create mode 100755 libsigrokdecode4DSL/decoders/0-uart/.pd.py.swp create mode 100755 libsigrokdecode4DSL/decoders/0-uart/__init__.py create mode 100755 libsigrokdecode4DSL/decoders/0-uart/pd.py create mode 100755 libsigrokdecode4DSL/decoders/1-i2c/.pd.py.swo create mode 100755 libsigrokdecode4DSL/decoders/1-i2c/.pd.py.swp create mode 100755 libsigrokdecode4DSL/decoders/1-i2c/__init__.py create mode 100755 libsigrokdecode4DSL/decoders/1-i2c/pd.py create mode 100755 libsigrokdecode4DSL/decoders/1-spi/.pd.py.swn create mode 100755 libsigrokdecode4DSL/decoders/1-spi/.pd.py.swo create mode 100755 libsigrokdecode4DSL/decoders/1-spi/.pd.py.swp create mode 100755 libsigrokdecode4DSL/decoders/1-spi/__init__.py create mode 100755 libsigrokdecode4DSL/decoders/1-spi/pd.py create mode 100755 libsigrokdecode4DSL/decoders/1-uart/.pd.py.swo create mode 100755 libsigrokdecode4DSL/decoders/1-uart/.pd.py.swp create mode 100755 libsigrokdecode4DSL/decoders/1-uart/__init__.py create mode 100755 libsigrokdecode4DSL/decoders/1-uart/pd.py create mode 100755 libsigrokdecode4DSL/decoders/adns5020/__init__.py create mode 100755 libsigrokdecode4DSL/decoders/adns5020/pd.py create mode 100755 libsigrokdecode4DSL/decoders/am230x/__init__.py create mode 100755 libsigrokdecode4DSL/decoders/am230x/pd.py create mode 100755 libsigrokdecode4DSL/decoders/arm_etmv3/__init__.py create mode 100755 libsigrokdecode4DSL/decoders/arm_etmv3/pd.py create mode 100755 libsigrokdecode4DSL/decoders/arm_itm/__init__.py create mode 100755 libsigrokdecode4DSL/decoders/arm_itm/pd.py create mode 100755 libsigrokdecode4DSL/decoders/arm_tpiu/__init__.py create mode 100755 libsigrokdecode4DSL/decoders/arm_tpiu/pd.py create mode 100755 libsigrokdecode4DSL/decoders/aud/__init__.py create mode 100755 libsigrokdecode4DSL/decoders/aud/pd.py create mode 100755 libsigrokdecode4DSL/decoders/avr_isp/__init__.py create mode 100755 libsigrokdecode4DSL/decoders/avr_isp/parts.py create mode 100755 libsigrokdecode4DSL/decoders/avr_isp/pd.py create mode 100755 libsigrokdecode4DSL/decoders/can/__init__.py create mode 100755 libsigrokdecode4DSL/decoders/can/pd.py create mode 100755 libsigrokdecode4DSL/decoders/dcf77/__init__.py create mode 100755 libsigrokdecode4DSL/decoders/dcf77/pd.py create mode 100755 libsigrokdecode4DSL/decoders/ds1307/__init__.py create mode 100755 libsigrokdecode4DSL/decoders/ds1307/pd.py create mode 100755 libsigrokdecode4DSL/decoders/edid/__init__.py create mode 100755 libsigrokdecode4DSL/decoders/edid/config create mode 100755 libsigrokdecode4DSL/decoders/edid/pd.py create mode 100755 libsigrokdecode4DSL/decoders/edid/pnpids.txt create mode 100755 libsigrokdecode4DSL/decoders/eeprom24xx/__init__.py create mode 100755 libsigrokdecode4DSL/decoders/eeprom24xx/lists.py create mode 100755 libsigrokdecode4DSL/decoders/eeprom24xx/pd.py create mode 100755 libsigrokdecode4DSL/decoders/em4100/__init__.py create mode 100755 libsigrokdecode4DSL/decoders/em4100/pd.py create mode 100755 libsigrokdecode4DSL/decoders/guess_bitrate/__init__.py create mode 100755 libsigrokdecode4DSL/decoders/guess_bitrate/pd.py create mode 100755 libsigrokdecode4DSL/decoders/i2cdemux/__init__.py create mode 100755 libsigrokdecode4DSL/decoders/i2cdemux/pd.py create mode 100755 libsigrokdecode4DSL/decoders/i2cfilter/__init__.py create mode 100755 libsigrokdecode4DSL/decoders/i2cfilter/pd.py create mode 100755 libsigrokdecode4DSL/decoders/i2s/__init__.py create mode 100755 libsigrokdecode4DSL/decoders/i2s/pd.py create mode 100755 libsigrokdecode4DSL/decoders/ir_nec/__init__.py create mode 100755 libsigrokdecode4DSL/decoders/ir_nec/lists.py create mode 100755 libsigrokdecode4DSL/decoders/ir_nec/pd.py create mode 100755 libsigrokdecode4DSL/decoders/ir_rc5/__init__.py create mode 100755 libsigrokdecode4DSL/decoders/ir_rc5/lists.py create mode 100755 libsigrokdecode4DSL/decoders/ir_rc5/pd.py create mode 100755 libsigrokdecode4DSL/decoders/jitter/__init__.py create mode 100755 libsigrokdecode4DSL/decoders/jitter/pd.py create mode 100755 libsigrokdecode4DSL/decoders/jtag/__init__.py create mode 100755 libsigrokdecode4DSL/decoders/jtag/pd.py create mode 100755 libsigrokdecode4DSL/decoders/jtag_stm32/__init__.py create mode 100755 libsigrokdecode4DSL/decoders/jtag_stm32/pd.py create mode 100755 libsigrokdecode4DSL/decoders/lm75/__init__.py create mode 100755 libsigrokdecode4DSL/decoders/lm75/pd.py create mode 100755 libsigrokdecode4DSL/decoders/lpc/__init__.py create mode 100755 libsigrokdecode4DSL/decoders/lpc/pd.py create mode 100755 libsigrokdecode4DSL/decoders/max7219/__init__.py create mode 100755 libsigrokdecode4DSL/decoders/max7219/pd.py create mode 100755 libsigrokdecode4DSL/decoders/maxim_ds28ea00/__init__.py create mode 100755 libsigrokdecode4DSL/decoders/maxim_ds28ea00/pd.py create mode 100755 libsigrokdecode4DSL/decoders/mdio/__init__.py create mode 100755 libsigrokdecode4DSL/decoders/mdio/pd.py create mode 100755 libsigrokdecode4DSL/decoders/midi/__init__.py create mode 100755 libsigrokdecode4DSL/decoders/midi/lists.py create mode 100755 libsigrokdecode4DSL/decoders/midi/pd.py create mode 100755 libsigrokdecode4DSL/decoders/mlx90614/__init__.py create mode 100755 libsigrokdecode4DSL/decoders/mlx90614/pd.py create mode 100755 libsigrokdecode4DSL/decoders/modbus/__init__.py create mode 100755 libsigrokdecode4DSL/decoders/modbus/pd.py create mode 100755 libsigrokdecode4DSL/decoders/mrf24j40/__init__.py create mode 100755 libsigrokdecode4DSL/decoders/mrf24j40/lists.py create mode 100755 libsigrokdecode4DSL/decoders/mrf24j40/pd.py create mode 100755 libsigrokdecode4DSL/decoders/mxc6225xu/__init__.py create mode 100755 libsigrokdecode4DSL/decoders/mxc6225xu/pd.py create mode 100755 libsigrokdecode4DSL/decoders/nrf24l01/__init__.py create mode 100755 libsigrokdecode4DSL/decoders/nrf24l01/pd.py create mode 100755 libsigrokdecode4DSL/decoders/nunchuk/__init__.py create mode 100755 libsigrokdecode4DSL/decoders/nunchuk/pd.py create mode 100755 libsigrokdecode4DSL/decoders/onewire_link/__init__.py create mode 100755 libsigrokdecode4DSL/decoders/onewire_link/pd.py create mode 100755 libsigrokdecode4DSL/decoders/onewire_link/pd.py.back create mode 100755 libsigrokdecode4DSL/decoders/onewire_network/__init__.py create mode 100755 libsigrokdecode4DSL/decoders/onewire_network/pd.py create mode 100755 libsigrokdecode4DSL/decoders/pan1321/__init__.py create mode 100755 libsigrokdecode4DSL/decoders/pan1321/pd.py create mode 100755 libsigrokdecode4DSL/decoders/parallel/__init__.py create mode 100755 libsigrokdecode4DSL/decoders/parallel/pd.py create mode 100755 libsigrokdecode4DSL/decoders/pwm/__init__.py create mode 100755 libsigrokdecode4DSL/decoders/pwm/pd.py create mode 100755 libsigrokdecode4DSL/decoders/qi/__init__.py create mode 100755 libsigrokdecode4DSL/decoders/qi/pd.py create mode 100755 libsigrokdecode4DSL/decoders/rfm12/__init__.py create mode 100755 libsigrokdecode4DSL/decoders/rfm12/pd.py create mode 100755 libsigrokdecode4DSL/decoders/rgb_led_spi/__init__.py create mode 100755 libsigrokdecode4DSL/decoders/rgb_led_spi/pd.py create mode 100755 libsigrokdecode4DSL/decoders/rgb_led_ws281x/__init__.py create mode 100755 libsigrokdecode4DSL/decoders/rgb_led_ws281x/pd.py create mode 100755 libsigrokdecode4DSL/decoders/rtc8564/__init__.py create mode 100755 libsigrokdecode4DSL/decoders/rtc8564/pd.py create mode 100755 libsigrokdecode4DSL/decoders/sdcard_sd/__init__.py create mode 100755 libsigrokdecode4DSL/decoders/sdcard_sd/lists.py create mode 100755 libsigrokdecode4DSL/decoders/sdcard_sd/pd.py create mode 100755 libsigrokdecode4DSL/decoders/sdcard_spi/__init__.py create mode 100755 libsigrokdecode4DSL/decoders/sdcard_spi/pd.py create mode 100755 libsigrokdecode4DSL/decoders/spdif/__init__.py create mode 100755 libsigrokdecode4DSL/decoders/spdif/pd.py create mode 100755 libsigrokdecode4DSL/decoders/spi/__init__.py create mode 100755 libsigrokdecode4DSL/decoders/spi/pd.py create mode 100755 libsigrokdecode4DSL/decoders/spiflash/__init__.py create mode 100755 libsigrokdecode4DSL/decoders/spiflash/lists.py create mode 100755 libsigrokdecode4DSL/decoders/spiflash/pd.py create mode 100755 libsigrokdecode4DSL/decoders/stepper_motor/__init__.py create mode 100755 libsigrokdecode4DSL/decoders/stepper_motor/pd.py create mode 100755 libsigrokdecode4DSL/decoders/swd/__init__.py create mode 100755 libsigrokdecode4DSL/decoders/swd/pd.py create mode 100755 libsigrokdecode4DSL/decoders/t55xx/__init__.py create mode 100755 libsigrokdecode4DSL/decoders/t55xx/pd.py create mode 100755 libsigrokdecode4DSL/decoders/tca6408a/__init__.py create mode 100755 libsigrokdecode4DSL/decoders/tca6408a/pd.py create mode 100755 libsigrokdecode4DSL/decoders/timing/__init__.py create mode 100755 libsigrokdecode4DSL/decoders/timing/pd.py create mode 100755 libsigrokdecode4DSL/decoders/tlc5620/__init__.py create mode 100755 libsigrokdecode4DSL/decoders/tlc5620/pd.py create mode 100755 libsigrokdecode4DSL/decoders/usb_packet/__init__.py create mode 100755 libsigrokdecode4DSL/decoders/usb_packet/pd.py create mode 100755 libsigrokdecode4DSL/decoders/usb_power_delivery/__init__.py create mode 100755 libsigrokdecode4DSL/decoders/usb_power_delivery/pd.py create mode 100755 libsigrokdecode4DSL/decoders/usb_request/__init__.py create mode 100755 libsigrokdecode4DSL/decoders/usb_request/pd.py create mode 100755 libsigrokdecode4DSL/decoders/usb_signalling/__init__.py create mode 100755 libsigrokdecode4DSL/decoders/usb_signalling/pd.py create mode 100755 libsigrokdecode4DSL/decoders/wiegand/__init__.py create mode 100755 libsigrokdecode4DSL/decoders/wiegand/pd.py create mode 100755 libsigrokdecode4DSL/decoders/xfp/__init__.py create mode 100755 libsigrokdecode4DSL/decoders/xfp/pd.py create mode 100755 libsigrokdecode4DSL/decoders/z80/__init__.py create mode 100755 libsigrokdecode4DSL/decoders/z80/pd.py create mode 100755 libsigrokdecode4DSL/decoders/z80/tables.py create mode 100644 libsigrokdecode4DSL/error.c create mode 100644 libsigrokdecode4DSL/exception.c create mode 100644 libsigrokdecode4DSL/instance.c create mode 100644 libsigrokdecode4DSL/libsigrokdecode-internal.h create mode 100644 libsigrokdecode4DSL/libsigrokdecode.h create mode 100644 libsigrokdecode4DSL/log.c create mode 100644 libsigrokdecode4DSL/m4/sigrok.m4 create mode 100644 libsigrokdecode4DSL/module_sigrokdecode.c create mode 100644 libsigrokdecode4DSL/session.c create mode 100644 libsigrokdecode4DSL/srd.c create mode 100644 libsigrokdecode4DSL/tests/core.c create mode 100644 libsigrokdecode4DSL/tests/decoder.c create mode 100644 libsigrokdecode4DSL/tests/inst.c rename DSView/pv/dialogs/messagebox.h => libsigrokdecode4DSL/tests/lib.h (51%) create mode 100644 libsigrokdecode4DSL/tests/main.c create mode 100644 libsigrokdecode4DSL/tests/session.c create mode 100755 libsigrokdecode4DSL/tools/install-decoders create mode 100644 libsigrokdecode4DSL/type_decoder.c create mode 100644 libsigrokdecode4DSL/type_logic.c create mode 100644 libsigrokdecode4DSL/util.c create mode 100644 libsigrokdecode4DSL/version.c create mode 100644 libsigrokdecode4DSL/version.h.in diff --git a/DSView/CMakeLists.txt b/DSView/CMakeLists.txt index c5703c64..c8afe9df 100644 --- a/DSView/CMakeLists.txt +++ b/DSView/CMakeLists.txt @@ -93,6 +93,7 @@ find_package(Threads) find_package(Boost 1.42 COMPONENTS filesystem system thread REQUIRED) find_package(libusb-1.0 REQUIRED) +find_package(FFTW REQUIRED) #=============================================================================== #= Config Header @@ -118,107 +119,147 @@ configure_file ( #------------------------------------------------------------------------------- set(DSView_SOURCES - main.cpp - pv/devicemanager.cpp - pv/mainwindow.cpp - pv/sigsession.cpp - pv/storesession.cpp - pv/data/analog.cpp - pv/data/analogsnapshot.cpp - pv/data/dso.cpp - pv/data/dsosnapshot.cpp - pv/data/group.cpp - pv/data/groupsnapshot.cpp - pv/data/logic.cpp - pv/data/logicsnapshot.cpp - pv/data/signaldata.cpp - pv/data/snapshot.cpp - pv/device/devinst.cpp - pv/device/device.cpp - pv/device/file.cpp - pv/device/inputfile.cpp - pv/device/sessionfile.cpp - pv/dialogs/about.cpp - pv/dialogs/deviceoptions.cpp - pv/dialogs/search.cpp - pv/dialogs/storeprogress.cpp - pv/dialogs/streamoptions.cpp - pv/dialogs/waitingdialog.cpp - pv/dialogs/dsomeasure.cpp - pv/dock/dsotriggerdock.cpp - pv/dock/measuredock.cpp - pv/dock/searchdock.cpp - pv/dock/triggerdock.cpp - pv/prop/bool.cpp - pv/prop/double.cpp - pv/prop/enum.cpp - pv/prop/int.cpp - pv/prop/property.cpp - pv/prop/string.cpp - pv/prop/binding/binding.cpp - pv/prop/binding/binding_deviceoptions.cpp - pv/toolbars/filebar.cpp - pv/toolbars/logobar.cpp - pv/toolbars/samplingbar.cpp - pv/toolbars/trigbar.cpp - pv/view/analogsignal.cpp - pv/view/cursor.cpp - pv/view/devmode.cpp - pv/view/dsldial.cpp - pv/view/dsosignal.cpp - pv/view/groupsignal.cpp - pv/view/header.cpp - pv/view/logicsignal.cpp - pv/view/ruler.cpp - pv/view/selectableitem.cpp - pv/view/signal.cpp - pv/view/timemarker.cpp - pv/view/trace.cpp - pv/view/view.cpp - pv/view/viewport.cpp - pv/widgets/fakelineedit.cpp + main.cpp + pv/sigsession.cpp + pv/mainwindow.cpp + pv/devicemanager.cpp + pv/data/snapshot.cpp + pv/data/signaldata.cpp + pv/data/logicsnapshot.cpp + pv/data/logic.cpp + pv/data/analogsnapshot.cpp + pv/data/analog.cpp + pv/dialogs/deviceoptions.cpp + pv/prop/property.cpp + pv/prop/int.cpp + pv/prop/enum.cpp + pv/prop/double.cpp + pv/prop/bool.cpp + pv/prop/binding/binding.cpp + pv/toolbars/samplingbar.cpp + pv/view/viewport.cpp + pv/view/view.cpp + pv/view/timemarker.cpp + pv/view/signal.cpp + pv/view/ruler.cpp + pv/view/logicsignal.cpp + pv/view/header.cpp + pv/view/cursor.cpp + pv/view/analogsignal.cpp + pv/prop/binding/binding_deviceoptions.cpp + pv/toolbars/trigbar.cpp + pv/toolbars/filebar.cpp + pv/dock/protocoldock.cpp + pv/dock/triggerdock.cpp + pv/dock/measuredock.cpp + pv/dock/searchdock.cpp + pv/toolbars/logobar.cpp + pv/data/groupsnapshot.cpp + pv/view/groupsignal.cpp + pv/data/group.cpp + pv/dialogs/about.cpp + pv/dialogs/search.cpp + pv/data/dsosnapshot.cpp + pv/data/dso.cpp + pv/view/dsosignal.cpp + pv/view/dsldial.cpp + pv/dock/dsotriggerdock.cpp + pv/view/trace.cpp + pv/view/selectableitem.cpp + pv/data/decoderstack.cpp + pv/data/decode/rowdata.cpp + pv/data/decode/row.cpp + pv/data/decode/decoder.cpp + pv/data/decode/annotation.cpp + pv/view/decodetrace.cpp + pv/prop/binding/decoderoptions.cpp + pv/widgets/fakelineedit.cpp + pv/widgets/decodermenu.cpp + pv/widgets/decodergroupbox.cpp + pv/prop/string.cpp + pv/device/sessionfile.cpp + pv/device/inputfile.cpp + pv/device/file.cpp + pv/device/devinst.cpp + pv/dialogs/storeprogress.cpp + pv/storesession.cpp + pv/view/devmode.cpp + pv/device/device.cpp + pv/dialogs/waitingdialog.cpp + pv/dialogs/dsomeasure.cpp + pv/dialogs/calibration.cpp + pv/data/decodermodel.cpp + pv/dialogs/protocollist.cpp + pv/dialogs/protocolexp.cpp + pv/dialogs/fftoptions.cpp + pv/data/mathstack.cpp + pv/view/mathtrace.cpp + dsapplication.cpp + pv/widgets/viewstatus.cpp + pv/toolbars/titlebar.cpp + pv/mainframe.cpp + pv/widgets/border.cpp + pv/dialogs/dsmessagebox.cpp + pv/dialogs/shadow.cpp + pv/dialogs/dsdialog.cpp ) set(DSView_HEADERS - pv/mainwindow.h - pv/sigsession.h - pv/storesession.h - pv/device/devinst.h - pv/dialogs/about.h - pv/dialogs/deviceoptions.h - pv/dialogs/search.h - pv/dialogs/storeprogress.h - pv/dialogs/streamoptions.h - pv/dialogs/waitingdialog.h - pv/dialogs/dsomeasure.h - pv/dock/dsotriggerdock.h - pv/dock/measuredock.h - pv/dock/searchdock.h - pv/dock/triggerdock.h - pv/prop/bool.h - pv/prop/double.h - pv/prop/enum.h - pv/prop/int.h - pv/prop/property.h - pv/prop/string.h - pv/toolbars/filebar.h - pv/toolbars/logobar.h - pv/toolbars/samplingbar.h - pv/toolbars/trigbar.h - pv/view/cursor.h - pv/view/devmode.h - pv/view/header.h - pv/view/ruler.h - pv/view/selectableitem.h - pv/view/timemarker.h - pv/view/trace.h - pv/view/view.h - pv/view/viewport.h - pv/widgets/fakelineedit.h + pv/sigsession.h + pv/mainwindow.h + pv/dialogs/deviceoptions.h + pv/prop/property.h + pv/prop/int.h + pv/prop/enum.h + pv/prop/double.h + pv/prop/bool.h + pv/toolbars/samplingbar.h + pv/view/viewport.h + pv/view/view.h + pv/view/timemarker.h + pv/view/ruler.h + pv/view/header.h + pv/view/cursor.h + pv/toolbars/trigbar.h + pv/toolbars/filebar.h + pv/dock/protocoldock.h + pv/dock/triggerdock.h + pv/dock/measuredock.h + pv/dock/searchdock.h + pv/toolbars/logobar.h + pv/dialogs/about.h + pv/dialogs/search.h + pv/dock/dsotriggerdock.h + pv/view/trace.h + pv/view/selectableitem.h + pv/data/decoderstack.h + pv/view/decodetrace.h + pv/widgets/fakelineedit.h + pv/widgets/decodermenu.h + pv/widgets/decodergroupbox.h + pv/prop/string.h + pv/device/devinst.h + pv/dialogs/storeprogress.h + pv/storesession.h + pv/view/devmode.h + pv/dialogs/waitingdialog.h + pv/dialogs/dsomeasure.h + pv/dialogs/calibration.h + pv/dialogs/protocollist.h + pv/dialogs/protocolexp.h + pv/dialogs/fftoptions.h + pv/data/mathstack.h + pv/view/mathtrace.h + pv/widgets/viewstatus.h + pv/toolbars/titlebar.h + pv/mainframe.h + pv/widgets/border.h + pv/dialogs/dsmessagebox.h + pv/dialogs/shadow.h + pv/dialogs/dsdialog.h ) set(DSView_FORMS - pv/dialogs/about.ui ) set(DSView_RESOURCES @@ -310,6 +351,7 @@ set(DSVIEW_LINK_LIBS ${CMAKE_THREAD_LIBS_INIT} ${QT_LIBRARIES} ${LIBUSB_1_LIBRARIES} + ${FFTW_LIBRARIES} ) if(STATIC_PKGDEPS_LIBS) diff --git a/DSView/darkstyle/style.qss b/DSView/darkstyle/style.qss index af69a9eb..6b3fc4ea 100755 --- a/DSView/darkstyle/style.qss +++ b/DSView/darkstyle/style.qss @@ -662,7 +662,7 @@ QAbstractSpinBox { background-color: #201F1F; color: silver; border-radius: 2px; - min-width: 50px; + min-width: 60px; } QAbstractSpinBox:up-button diff --git a/DSView/main.cpp b/DSView/main.cpp index 08d94c03..bb426cea 100644 --- a/DSView/main.cpp +++ b/DSView/main.cpp @@ -137,7 +137,7 @@ int main(int argc, char *argv[]) // Initialise the main frame pv::MainFrame w(device_manager, open_file); //QFile qss(":/stylesheet.qss"); - QFile qss(":qdarkstyle/style.qss"); + QFile qss(":darkstyle/style.qss"); qss.open(QFile::ReadOnly); a.setStyleSheet(qss.readAll()); qss.close(); diff --git a/DSView/pv/data/decoderstack.cpp b/DSView/pv/data/decoderstack.cpp index d463ecba..233dcc4d 100644 --- a/DSView/pv/data/decoderstack.cpp +++ b/DSView/pv/data/decoderstack.cpp @@ -117,7 +117,7 @@ void DecoderStack::remove(boost::shared_ptr &decoder) { // Find the decoder in the stack list< shared_ptr >::iterator iter = _stack.begin(); - for(int i = 0; i < _stack.size(); i++, iter++) + for(unsigned int i = 0; i < _stack.size(); i++, iter++) if ((*iter) == decoder) break; @@ -285,7 +285,6 @@ uint64_t DecoderStack::list_annotation_size() const { lock_guard lock(_output_mutex); uint64_t max_annotation_size = 0; - int row = 0; for (map::const_iterator i = _rows.begin(); i != _rows.end(); i++) { map::const_iterator iter = _rows_lshow.find((*i).first); @@ -316,7 +315,6 @@ bool DecoderStack::list_annotation(pv::data::decode::Annotation &ann, uint16_t row_index, uint64_t col_index) const { //lock_guard lock(_output_mutex); - int row = 0; for (map::const_iterator i = _rows.begin(); i != _rows.end(); i++) { map::const_iterator iter = _rows_lshow.find((*i).first); @@ -334,7 +332,6 @@ bool DecoderStack::list_annotation(pv::data::decode::Annotation &ann, bool DecoderStack::list_row_title(int row, QString &title) const { //lock_guard lock(_output_mutex); - int index = 0; for (map::const_iterator i = _rows.begin(); i != _rows.end(); i++) { map::const_iterator iter = _rows_lshow.find((*i).first); @@ -574,8 +571,8 @@ void DecoderStack::decode_proc() optional sample_count; srd_session *session; srd_decoder_inst *prev_di = NULL; - uint64_t decode_start; - uint64_t decode_end; + uint64_t decode_start = 0; + uint64_t decode_end = 0; assert(_snapshot); @@ -726,7 +723,6 @@ int DecoderStack::list_rows_size() { //lock_guard lock(_output_mutex); int rows_size = 0; - int row = 0; for (map::const_iterator i = _rows.begin(); i != _rows.end(); i++) { map::const_iterator iter = _rows_lshow.find((*i).first); diff --git a/DSView/pv/data/decoderstack.h b/DSView/pv/data/decoderstack.h index afaa041a..281a5428 100644 --- a/DSView/pv/data/decoderstack.h +++ b/DSView/pv/data/decoderstack.h @@ -32,9 +32,9 @@ #include #include -#include <../data/decode/row.h> -#include <../data/decode/rowdata.h> -#include <../data/signaldata.h> +#include "../data/decode/row.h" +#include "../data/decode/rowdata.h" +#include "../data/signaldata.h" namespace DecoderStackTest { class TwoDecoderStack; diff --git a/DSView/pv/data/dsosnapshot.cpp b/DSView/pv/data/dsosnapshot.cpp index 0bd6c6c2..665c0dbe 100644 --- a/DSView/pv/data/dsosnapshot.cpp +++ b/DSView/pv/data/dsosnapshot.cpp @@ -166,7 +166,7 @@ void DsoSnapshot::enable_envelope(bool enable) const uint8_t *DsoSnapshot::get_samples( int64_t start_sample, int64_t end_sample, uint16_t index) const { - (void)end_sample; + (void)end_sample; assert(start_sample >= 0); assert(start_sample < (int64_t)get_sample_count()); @@ -202,9 +202,9 @@ void DsoSnapshot::get_envelope_section(EnvelopeSection &s, s.start = start << scale_power; s.scale = 1 << scale_power; - //if (_envelope_levels[probe_index][min_level].length < get_sample_count() / EnvelopeScaleFactor) - // s.length = 0; - //else + if (_envelope_levels[probe_index][min_level].length == 0) + s.length = 0; + else s.length = end - start; // s.samples = new EnvelopeSample[s.length]; // memcpy(s.samples, _envelope_levels[min_level].samples + start, diff --git a/DSView/pv/data/mathstack.cpp b/DSView/pv/data/mathstack.cpp index f15745ef..363232d7 100644 --- a/DSView/pv/data/mathstack.cpp +++ b/DSView/pv/data/mathstack.cpp @@ -205,7 +205,7 @@ void MathStack::calc_fft() const uint16_t step = _snapshot->get_channel_num() * _sample_interval; const uint8_t *const samples = _snapshot->get_samples(0, _sample_num*_sample_interval-1, _index); double wsum = 0; - for (int i = 0; i < _sample_num; i++) { + for (unsigned int i = 0; i < _sample_num; i++) { double w = window(i, _windows_index); _xn[i] = ((double)samples[i*step] - offset) * vscale * w; wsum += w; @@ -216,7 +216,7 @@ void MathStack::calc_fft() // calculate power spectrum _power_spectrum[0] = abs(_xk[0])/wsum; /* DC component */ - for (int k = 1; k < (_sample_num + 1) / 2; ++k) /* (k < N/2 rounded up) */ + for (unsigned int k = 1; k < (_sample_num + 1) / 2; ++k) /* (k < N/2 rounded up) */ _power_spectrum[k] = sqrt((_xk[k]*_xk[k] + _xk[_sample_num-k]*_xk[_sample_num-k]) * 2) / wsum; if (_sample_num % 2 == 0) /* N is even */ _power_spectrum[_sample_num/2] = abs(_xk[_sample_num/2])/wsum; /* Nyquist freq. */ diff --git a/DSView/pv/device/device.cpp b/DSView/pv/device/device.cpp index 76fd520b..74b021f8 100644 --- a/DSView/pv/device/device.cpp +++ b/DSView/pv/device/device.cpp @@ -44,14 +44,14 @@ sr_dev_inst* Device::dev_inst() const void Device::use(SigSession *owner) throw(QString) { - DevInst::use(owner); + DevInst::use(owner); - sr_session_new(); + sr_session_new(); - assert(_sdi); - sr_dev_open(_sdi); - if (sr_session_dev_add(_sdi) != SR_OK) - throw QString(tr("Failed to use device.")); + assert(_sdi); + sr_dev_open(_sdi); + if (sr_session_dev_add(_sdi) != SR_OK) + throw QString(tr("Failed to use device.")); } void Device::release() diff --git a/DSView/pv/dialogs/about.ui b/DSView/pv/dialogs/about.ui deleted file mode 100644 index d4fef85d..00000000 --- a/DSView/pv/dialogs/about.ui +++ /dev/null @@ -1,69 +0,0 @@ - - - About - - - Qt::WindowModal - - - - 0 - 0 - 600 - 320 - - - - - 600 - 320 - - - - About - - - - - - - - - - - - - - :/icons/dsl_logo.png - - - Qt::AlignCenter - - - - - - - - - - Qt::AlignCenter - - - - - - - QDialogButtonBox::Ok - - - - - - - - - - - - diff --git a/DSView/pv/dialogs/calibration.cpp b/DSView/pv/dialogs/calibration.cpp index 465affe6..b25b7ea7 100755 --- a/DSView/pv/dialogs/calibration.cpp +++ b/DSView/pv/dialogs/calibration.cpp @@ -98,8 +98,8 @@ void Calibration::set_device(boost::shared_ptr dev_inst) sr_channel *const probe = (sr_channel*)l->data; assert(probe); - uint64_t vgain, vgain_default; - uint16_t vgain_range; + uint64_t vgain = 0, vgain_default = 0; + uint16_t vgain_range = 0; GVariant* gvar = _dev_inst->get_config(probe, NULL, SR_CONF_VGAIN); if (gvar != NULL) { vgain = g_variant_get_uint64(gvar); @@ -126,18 +126,13 @@ void Calibration::set_device(boost::shared_ptr dev_inst) _slider_list.push_back(gain_slider); _label_list.push_back(gain_label); - uint64_t voff, voff_default; - uint16_t voff_range; + uint64_t voff = 0; + uint16_t voff_range = 0; gvar = _dev_inst->get_config(probe, NULL, SR_CONF_VOFF); if (gvar != NULL) { voff = g_variant_get_uint16(gvar); g_variant_unref(gvar); } - gvar = _dev_inst->get_config(probe, NULL, SR_CONF_VOFF_DEFAULT); - if (gvar != NULL) { - voff_default = g_variant_get_uint16(gvar); - g_variant_unref(gvar); - } gvar = _dev_inst->get_config(probe, NULL, SR_CONF_VOFF_RANGE); if (gvar != NULL) { voff_range = g_variant_get_uint16(gvar); @@ -204,10 +199,10 @@ void Calibration::on_save() this->hide(); QFuture future; future = QtConcurrent::run([&]{ - QTime dieTime = QTime::currentTime().addSecs(1); + //QTime dieTime = QTime::currentTime().addSecs(1); _dev_inst->set_config(NULL, NULL, SR_CONF_ZERO_SET, g_variant_new_boolean(true)); - while( QTime::currentTime() < dieTime ); + //while( QTime::currentTime() < dieTime ); }); Qt::WindowFlags flags = Qt::CustomizeWindowHint; QProgressDialog dlg(tr("Save Calibration Result... It can take a while."), @@ -229,11 +224,11 @@ void Calibration::on_reset() this->hide(); QFuture future; future = QtConcurrent::run([&]{ - QTime dieTime = QTime::currentTime().addSecs(1); + //QTime dieTime = QTime::currentTime().addSecs(1); _dev_inst->set_config(NULL, NULL, SR_CONF_ZERO_LOAD, g_variant_new_boolean(true)); reload_value(); - while( QTime::currentTime() < dieTime ); + //while( QTime::currentTime() < dieTime ); }); Qt::WindowFlags flags = Qt::CustomizeWindowHint; QProgressDialog dlg(tr("Reset Calibration Result... It can take a while."), @@ -256,8 +251,8 @@ void Calibration::reload_value() sr_channel *const probe = (sr_channel*)l->data; assert(probe); - uint64_t vgain, vgain_default; - uint16_t vgain_range; + uint64_t vgain = 0, vgain_default = 0; + uint16_t vgain_range = 0; GVariant* gvar = _dev_inst->get_config(probe, NULL, SR_CONF_VGAIN); if (gvar != NULL) { vgain = g_variant_get_uint64(gvar); @@ -274,8 +269,8 @@ void Calibration::reload_value() g_variant_unref(gvar); } - uint64_t voff; - uint16_t voff_range; + uint64_t voff = 0; + uint16_t voff_range = 0; gvar = _dev_inst->get_config(probe, NULL, SR_CONF_VOFF); if (gvar != NULL) { voff = g_variant_get_uint16(gvar); diff --git a/DSView/pv/dialogs/dsdialog.cpp b/DSView/pv/dialogs/dsdialog.cpp index a8f0202e..01f4a685 100755 --- a/DSView/pv/dialogs/dsdialog.cpp +++ b/DSView/pv/dialogs/dsdialog.cpp @@ -62,6 +62,7 @@ void DSDialog::reject() bool DSDialog::eventFilter(QObject *object, QEvent *event) { + (void)object; const QEvent::Type type = event->type(); const QMouseEvent *const mouse_event = (QMouseEvent*)event; if (type == QEvent::MouseMove) { diff --git a/DSView/pv/dialogs/dsmessagebox.cpp b/DSView/pv/dialogs/dsmessagebox.cpp index d7e44441..0491ba3d 100755 --- a/DSView/pv/dialogs/dsmessagebox.cpp +++ b/DSView/pv/dialogs/dsmessagebox.cpp @@ -83,6 +83,7 @@ void DSMessageBox::reject() bool DSMessageBox::eventFilter(QObject *object, QEvent *event) { + (void)object; const QEvent::Type type = event->type(); const QMouseEvent *const mouse_event = (QMouseEvent*)event; if (type == QEvent::MouseMove) { diff --git a/DSView/pv/dialogs/dsomeasure.cpp b/DSView/pv/dialogs/dsomeasure.cpp index f59e7c9a..ec04ee33 100644 --- a/DSView/pv/dialogs/dsomeasure.cpp +++ b/DSView/pv/dialogs/dsomeasure.cpp @@ -43,7 +43,7 @@ DsoMeasure::DsoMeasure(QWidget *parent, boost::shared_ptr dsoSig) : { setMinimumWidth(300); - for (int i=DsoSignal::DSO_MS_BEGIN+1; iget_ms_string(i), this); checkBox->setProperty("id", QVariant(i)); checkBox->setChecked(dsoSig->get_ms_en(i)); diff --git a/DSView/pv/dialogs/fftoptions.cpp b/DSView/pv/dialogs/fftoptions.cpp index ebe8b0b1..3b08406c 100644 --- a/DSView/pv/dialogs/fftoptions.cpp +++ b/DSView/pv/dialogs/fftoptions.cpp @@ -90,12 +90,12 @@ FftOptions::FftOptions(QWidget *parent, SigSession &session) : assert(length.size() > 0); assert(view_modes.size() > 0); assert(dbv_ranges.size() > 0); - for (int i = 0; i < windows.size(); i++) + for (unsigned int i = 0; i < windows.size(); i++) { _window_combobox->addItem(windows[i], qVariantFromValue(i)); } - for (int i = 0; i < length.size(); i++) + for (unsigned int i = 0; i < length.size(); i++) { if (length[i] < _sample_limit) _len_combobox->addItem(QString::number(length[i]), @@ -112,14 +112,14 @@ FftOptions::FftOptions(QWidget *parent, SigSession &session) : _interval_combobox->addItem(QString::number(i), qVariantFromValue(i)); } - for (int i = 0; i < view_modes.size(); i++) + for (unsigned int i = 0; i < view_modes.size(); i++) { _view_combobox->addItem(view_modes[i], qVariantFromValue(i)); } assert(_view_combobox->count() > 0); _view_combobox->setCurrentIndex(_view_combobox->count()-1); - for (int i = 0; i < dbv_ranges.size(); i++) + for (unsigned int i = 0; i < dbv_ranges.size(); i++) { _dbv_combobox->addItem(QString::number(dbv_ranges[i]), qVariantFromValue(dbv_ranges[i])); @@ -138,7 +138,7 @@ FftOptions::FftOptions(QWidget *parent, SigSession &session) : } } for (int i = 0; i < _len_combobox->count(); i++) { - if (mathTrace->get_math_stack()->get_sample_num() == _len_combobox->itemData(i).toLongLong()) { + if (mathTrace->get_math_stack()->get_sample_num() == _len_combobox->itemData(i).toULongLong()) { _len_combobox->setCurrentIndex(i); break; } diff --git a/DSView/pv/dialogs/messagebox.cpp b/DSView/pv/dialogs/messagebox.cpp deleted file mode 100644 index d0ab4eda..00000000 --- a/DSView/pv/dialogs/messagebox.cpp +++ /dev/null @@ -1,86 +0,0 @@ -/* - * This file is part of the DSView project. - * DSView is based on PulseView. - * - * Copyright (C) 2016 DreamSourceLab - * - * 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 "messagebox.h" - -#include -#include - -namespace pv { -namespace dialogs { - -MessageBox::MessageBox(QWidget *parent) : - QDialog(parent) -{ - setWindowFlags(Qt::FramelessWindowHint | Qt::Dialog); - - _msg = new QMessageBox(this); - _msg->setWindowFlags(Qt::FramelessWindowHint | Qt::Widget); - - _titlebar = new toolbars::TitleBar(false, parent); - _titlebar->setTitle(tr("Message")); - - QVBoxLayout *layout = new QVBoxLayout(this); - layout->addWidget(_titlebar); - layout->addWidget(_msg); - setLayout(layout); - - //connect(_msg, SIGNAL(finished(int)), this, SLOT(accept())); - connect(_msg, SIGNAL(buttonClicked(QAbstractButton*)), this, SLOT(on_button(QAbstractButton*))); -} - -void MessageBox::accept() -{ - using namespace Qt; - - QDialog::accept(); -} - -void MessageBox::reject() -{ - using namespace Qt; - - QDialog::reject(); -} - -QMessageBox* MessageBox::mBox() -{ - return _msg; -} - -int MessageBox::exec() -{ - //_msg->show(); - return QDialog::exec(); -} - -void MessageBox::on_button(QAbstractButton *btn) -{ - QMessageBox::ButtonRole role = _msg->buttonRole(btn); - if (role == QMessageBox::AcceptRole) - accept(); - else - reject(); -} - -} // namespace dialogs -} // namespace pv diff --git a/DSView/pv/dialogs/protocolexp.cpp b/DSView/pv/dialogs/protocolexp.cpp index d0907af8..2f7ec220 100644 --- a/DSView/pv/dialogs/protocolexp.cpp +++ b/DSView/pv/dialogs/protocolexp.cpp @@ -139,7 +139,7 @@ void ProtocolExp::accept() future = QtConcurrent::run([&]{ _export_cancel = false; QString title; - int index; + int index = 0; for (std::list::const_iterator i = _row_sel_list.begin(); i != _row_sel_list.end(); i++) { if ((*i)->isChecked()) { diff --git a/DSView/pv/dialogs/waitingdialog.cpp b/DSView/pv/dialogs/waitingdialog.cpp index 7996b988..54035be1 100644 --- a/DSView/pv/dialogs/waitingdialog.cpp +++ b/DSView/pv/dialogs/waitingdialog.cpp @@ -91,10 +91,10 @@ void WaitingDialog::accept() QFuture future; future = QtConcurrent::run([&]{ - QTime dieTime = QTime::currentTime().addSecs(1); + //QTime dieTime = QTime::currentTime().addSecs(1); _dev_inst->set_config(NULL, NULL, SR_CONF_ZERO_SET, g_variant_new_boolean(true)); - while( QTime::currentTime() < dieTime ); + //while( QTime::currentTime() < dieTime ); }); Qt::WindowFlags flags = Qt::CustomizeWindowHint; QProgressDialog dlg(tr("Save Auto Zero Result... It can take a while."), @@ -120,11 +120,11 @@ void WaitingDialog::reject() QFuture future; future = QtConcurrent::run([&]{ - QTime dieTime = QTime::currentTime().addSecs(1); + //QTime dieTime = QTime::currentTime().addSecs(1); _dev_inst->set_config(NULL, NULL, SR_CONF_ZERO, g_variant_new_boolean(false)); _dev_inst->set_config(NULL, NULL, SR_CONF_ZERO_LOAD, g_variant_new_boolean(true)); - while( QTime::currentTime() < dieTime ); + //while( QTime::currentTime() < dieTime ); }); Qt::WindowFlags flags = Qt::CustomizeWindowHint; QProgressDialog dlg(tr("Load Current Setting... It can take a while."), @@ -149,11 +149,11 @@ void WaitingDialog::stop() QDialog::reject(); } -void WaitingDialog::start() +int WaitingDialog::start() { movie->start(); timer->start(300); - this->exec(); + return this->exec(); } void WaitingDialog::changeText() diff --git a/DSView/pv/dialogs/waitingdialog.h b/DSView/pv/dialogs/waitingdialog.h index b611c2c4..68130835 100644 --- a/DSView/pv/dialogs/waitingdialog.h +++ b/DSView/pv/dialogs/waitingdialog.h @@ -53,7 +53,7 @@ private: public: WaitingDialog(QWidget *parent, boost::shared_ptr dev_inst); - void start(); + int start(); protected: void accept(); diff --git a/DSView/pv/dock/dsotriggerdock.cpp b/DSView/pv/dock/dsotriggerdock.cpp index d46cfa5c..39782c05 100644 --- a/DSView/pv/dock/dsotriggerdock.cpp +++ b/DSView/pv/dock/dsotriggerdock.cpp @@ -24,6 +24,10 @@ #include "../sigsession.h" #include "../device/devinst.h" #include "../dialogs/dsmessagebox.h" +#include "../view/dsosignal.h" + +#include +#include #include #include @@ -37,6 +41,9 @@ #include "libsigrok4DSL/libsigrok.h" +using namespace boost; +using namespace std; + namespace pv { namespace dock { @@ -61,6 +68,7 @@ DsoTriggerDock::DsoTriggerDock(QWidget *parent, SigSession &session) : holdoff_comboBox->addItem(tr("uS"), qVariantFromValue(1000)); holdoff_comboBox->addItem(tr("mS"), qVariantFromValue(1000000)); holdoff_comboBox->addItem(tr("S"), qVariantFromValue(1000000000)); + holdoff_comboBox->setCurrentIndex(0); holdoff_spinBox = new QSpinBox(_widget); holdoff_spinBox->setRange(0, 999); holdoff_spinBox->setButtonSymbols(QAbstractSpinBox::NoButtons); @@ -98,6 +106,7 @@ DsoTriggerDock::DsoTriggerDock(QWidget *parent, SigSession &session) : connect(falling_radioButton, SIGNAL(clicked()), this, SLOT(type_changed())); source_group=new QButtonGroup(_widget); + channel_comboBox = new QComboBox(_widget); type_group=new QButtonGroup(_widget); source_group->addButton(auto_radioButton); @@ -126,6 +135,7 @@ DsoTriggerDock::DsoTriggerDock(QWidget *parent, SigSession &session) : gLayout->addWidget(new QLabel(_widget), 2, 0); gLayout->addWidget(tSource_labe, 3, 0); gLayout->addWidget(auto_radioButton, 4, 0); + gLayout->addWidget(channel_comboBox, 4, 1, 1, 3); gLayout->addWidget(ch0_radioButton, 5, 0); gLayout->addWidget(ch1_radioButton, 5, 1, 1, 3); gLayout->addWidget(ch0a1_radioButton, 6, 0); @@ -245,6 +255,24 @@ void DsoTriggerDock::source_changed() } } +void DsoTriggerDock::channel_changed(int ch) +{ + (void)ch; + int ret; + + ret = _session.get_device()->set_config(NULL, NULL, + SR_CONF_TRIGGER_CHANNEL, + g_variant_new_byte(channel_comboBox->currentData().toInt())); + if (!ret) { + dialogs::DSMessageBox msg(this); + msg.mBox()->setText(tr("Trigger Setting Issue")); + msg.mBox()->setInformativeText(tr("Change trigger channel failed!")); + msg.mBox()->setStandardButtons(QMessageBox::Ok); + msg.mBox()->setIcon(QMessageBox::Warning); + msg.exec(); + } +} + void DsoTriggerDock::type_changed() { int id = type_group->checkedId(); @@ -285,6 +313,7 @@ void DsoTriggerDock::init() holdoff_spinBox->setDisabled(true); holdoff_comboBox->setDisabled(true); margin_slider->setDisabled(true); + channel_comboBox->setDisabled(true); return; } else { foreach(QAbstractButton * btn, source_group->buttons()) @@ -295,6 +324,7 @@ void DsoTriggerDock::init() holdoff_spinBox->setDisabled(false); holdoff_comboBox->setDisabled(false); margin_slider->setDisabled(false); + channel_comboBox->setDisabled(false); } // TRIGGERPOS @@ -314,6 +344,29 @@ void DsoTriggerDock::init() source_group->button(src)->setChecked(true); } + // setup channel_comboBox + disconnect(channel_comboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(channel_changed(int))); + channel_comboBox->clear(); + BOOST_FOREACH(const boost::shared_ptr s, _session.get_signals()) { + boost::shared_ptr dsoSig; + if (dsoSig = dynamic_pointer_cast(s)) { + channel_comboBox->addItem(dsoSig->get_name(), qVariantFromValue(dsoSig->get_index())); + } + } + gvar = _session.get_device()->get_config(NULL, NULL, + SR_CONF_TRIGGER_CHANNEL); + if (gvar != NULL) { + uint8_t src = g_variant_get_byte(gvar); + g_variant_unref(gvar); + for (int i = 0; i < channel_comboBox->count(); i++) { + if (src == channel_comboBox->itemData(i).toInt()) { + channel_comboBox->setCurrentIndex(i); + break; + } + } + } + connect(channel_comboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(channel_changed(int))); + gvar = _session.get_device()->get_config(NULL, NULL, SR_CONF_TRIGGER_SLOPE); if (gvar != NULL) { @@ -321,6 +374,39 @@ void DsoTriggerDock::init() g_variant_unref(gvar); type_group->button(slope)->setChecked(true); } + + disconnect(holdoff_slider, SIGNAL(valueChanged(int)), this, SLOT(hold_changed(int))); + disconnect(holdoff_comboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(hold_changed(int))); + gvar = _session.get_device()->get_config(NULL, NULL, + SR_CONF_TRIGGER_HOLDOFF); + if (gvar != NULL) { + uint64_t holdoff = g_variant_get_uint64(gvar); + g_variant_unref(gvar); + for (int i = holdoff_comboBox->count()-1; i >= 0; i--) { + if (holdoff >= holdoff_comboBox->itemData(i).toDouble()) { + holdoff_comboBox->setCurrentIndex(i); + break; + } + } + if (holdoff_comboBox->currentData().toDouble() == 1000000000) { + holdoff_slider->setRange(0, 10); + } else { + holdoff_slider->setRange(0, 999); + } + holdoff_spinBox->setValue(holdoff * 10.0/holdoff_comboBox->currentData().toDouble()); + } + connect(holdoff_slider, SIGNAL(valueChanged(int)), this, SLOT(hold_changed(int))); + connect(holdoff_comboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(hold_changed(int))); + + disconnect(margin_slider, SIGNAL(valueChanged(int)), this, SLOT(margin_changed(int))); + gvar = _session.get_device()->get_config(NULL, NULL, + SR_CONF_TRIGGER_MARGIN); + if (gvar != NULL) { + uint8_t margin = g_variant_get_byte(gvar); + g_variant_unref(gvar); + margin_slider->setValue(margin); + } + connect(margin_slider, SIGNAL(valueChanged(int)), this, SLOT(margin_changed(int))); } } // namespace dock diff --git a/DSView/pv/dock/dsotriggerdock.h b/DSView/pv/dock/dsotriggerdock.h index d33045de..5062f5d5 100644 --- a/DSView/pv/dock/dsotriggerdock.h +++ b/DSView/pv/dock/dsotriggerdock.h @@ -63,6 +63,7 @@ private slots: void margin_changed(int margin); void source_changed(); void type_changed(); + void channel_changed(int ch); private: @@ -81,6 +82,7 @@ private: QSlider *position_slider; QButtonGroup *source_group; + QComboBox *channel_comboBox; QButtonGroup *type_group; }; diff --git a/DSView/pv/dock/measuredock.cpp b/DSView/pv/dock/measuredock.cpp index 9706eb2d..35156573 100644 --- a/DSView/pv/dock/measuredock.cpp +++ b/DSView/pv/dock/measuredock.cpp @@ -206,9 +206,9 @@ void MeasureDock::cursor_update() index++; } - _t1_comboBox->setMinimumWidth(_t1_comboBox->sizeHint().width()+15); - _t2_comboBox->setMinimumWidth(_t2_comboBox->sizeHint().width()+15); - _t3_comboBox->setMinimumWidth(_t3_comboBox->sizeHint().width()+15); + _t1_comboBox->setMinimumWidth(_t1_comboBox->sizeHint().width()+30); + _t2_comboBox->setMinimumWidth(_t2_comboBox->sizeHint().width()+30); + _t3_comboBox->setMinimumWidth(_t3_comboBox->sizeHint().width()+30); if (_t1_last_index < _t1_comboBox->count()) _t1_comboBox->setCurrentIndex(_t1_last_index); diff --git a/DSView/pv/dock/protocoldock.cpp b/DSView/pv/dock/protocoldock.cpp index 179d35d7..7a32761b 100644 --- a/DSView/pv/dock/protocoldock.cpp +++ b/DSView/pv/dock/protocoldock.cpp @@ -414,7 +414,7 @@ void ProtocolDock::decoded_progress(int progress) { (void) progress; - int pg; + int pg = 0; QString err=""; const std::vector< boost::shared_ptr > decode_sigs( _session.get_decode_signals()); @@ -454,7 +454,7 @@ void ProtocolDock::update_model() else if (!decoder_model->getDecoderStack()) decoder_model->setDecoderStack(decode_sigs.at(0)->decoder()); else { - int index = 0; + unsigned int index = 0; BOOST_FOREACH(const boost::shared_ptr d, decode_sigs) { if (d->decoder() == decoder_model->getDecoderStack()) { decoder_model->setDecoderStack(d->decoder()); @@ -570,7 +570,7 @@ void ProtocolDock::search_pre() _cur_search_index = -1; return; } - int i; + int i = 0; uint64_t rowCount = _model_proxy.rowCount(); QModelIndex matchingIndex; pv::data::DecoderModel *decoder_model = _session.get_decoder_model(); @@ -626,7 +626,7 @@ void ProtocolDock::search_nxt() _cur_search_index = -1; return; } - int i; + int i = 0; uint64_t rowCount = _model_proxy.rowCount(); QModelIndex matchingIndex; pv::data::DecoderModel *decoder_model = _session.get_decoder_model(); diff --git a/DSView/pv/dock/searchdock.cpp b/DSView/pv/dock/searchdock.cpp index c0b1871b..e523abaf 100644 --- a/DSView/pv/dock/searchdock.cpp +++ b/DSView/pv/dock/searchdock.cpp @@ -37,6 +37,9 @@ #include #include #include +#include +#include +#include #include #include @@ -64,7 +67,7 @@ SearchDock::SearchDock(QWidget *parent, View &view, SigSession &session) : _nxt_button.setIcon(QIcon::fromTheme("searchDock", QIcon(":/icons/next.png"))); - QPushButton *_search_button = new QPushButton(this); + _search_button = new QPushButton(this); _search_button->setIcon(QIcon::fromTheme("searchDock", QIcon(":/icons/search.png"))); _search_button->setFixedWidth(_search_button->height()); @@ -109,6 +112,7 @@ void SearchDock::paintEvent(QPaintEvent *) void SearchDock::on_previous() { + bool ret; uint64_t last_pos; uint8_t *data; int unit_size; @@ -136,7 +140,22 @@ void SearchDock::on_previous() msg.exec(); return; } else { - const bool ret = search_value(data, unit_size, length, last_pos, 1, value); + QFuture future; + future = QtConcurrent::run([&]{ + ret = search_value(data, unit_size, length, last_pos, 1, value); + }); + Qt::WindowFlags flags = Qt::CustomizeWindowHint; + QProgressDialog dlg(tr("Search Previous..."), + tr("Cancel"),0,0,this,flags); + dlg.setWindowModality(Qt::WindowModal); + dlg.setWindowFlags(Qt::Dialog | Qt::FramelessWindowHint); + dlg.setCancelButton(NULL); + + QFutureWatcher watcher; + connect(&watcher,SIGNAL(finished()),&dlg,SLOT(cancel())); + watcher.setFuture(future); + dlg.exec(); + if (!ret) { dialogs::DSMessageBox msg(this); msg.mBox()->setText(tr("Search")); @@ -154,6 +173,7 @@ void SearchDock::on_previous() void SearchDock::on_next() { + bool ret; uint64_t last_pos; int unit_size; uint64_t length; @@ -180,7 +200,22 @@ void SearchDock::on_next() msg.exec(); return; } else { - const int ret = search_value(data, unit_size, length, last_pos, 0, value); + QFuture future; + future = QtConcurrent::run([&]{ + ret = search_value(data, unit_size, length, last_pos, 0, value); + }); + Qt::WindowFlags flags = Qt::CustomizeWindowHint; + QProgressDialog dlg(tr("Search Next..."), + tr("Cancel"),0,0,this,flags); + dlg.setWindowModality(Qt::WindowModal); + dlg.setWindowFlags(Qt::Dialog | Qt::FramelessWindowHint); + dlg.setCancelButton(NULL); + + QFutureWatcher watcher; + connect(&watcher,SIGNAL(finished()),&dlg,SLOT(cancel())); + watcher.setFuture(future); + dlg.exec(); + if (!ret) { dialogs::DSMessageBox msg(this); msg.mBox()->setText(tr("Search")); @@ -204,6 +239,9 @@ void SearchDock::on_set() _pattern.remove(QChar(' '), Qt::CaseInsensitive); _pattern = _pattern.toUpper(); _search_value->setText(_pattern); + + QFontMetrics fm = this->fontMetrics(); + _search_value->setFixedWidth(fm.width(_pattern)+_search_button->width()+20); } } diff --git a/DSView/pv/dock/searchdock.h b/DSView/pv/dock/searchdock.h index 2583cf1a..e541cd6b 100644 --- a/DSView/pv/dock/searchdock.h +++ b/DSView/pv/dock/searchdock.h @@ -90,6 +90,7 @@ private: QPushButton _pre_button; QPushButton _nxt_button; widgets::FakeLineEdit* _search_value; + QPushButton *_search_button; }; } // namespace dock diff --git a/DSView/pv/mainwindow.cpp b/DSView/pv/mainwindow.cpp index ad024189..9b587840 100644 --- a/DSView/pv/mainwindow.cpp +++ b/DSView/pv/mainwindow.cpp @@ -45,6 +45,7 @@ #include #include #include +#include #include "mainwindow.h" @@ -148,7 +149,7 @@ void MainWindow::setup_ui() connect(_file_bar, SIGNAL(save()), this, SLOT(on_save())); connect(_file_bar, SIGNAL(on_screenShot()), this, - SLOT(on_screenShot())); + SLOT(on_screenShot()), Qt::QueuedConnection); connect(_file_bar, SIGNAL(load_session(QString)), this, SLOT(load_session(QString))); connect(_file_bar, SIGNAL(store_session(QString)), this, @@ -244,9 +245,9 @@ void MainWindow::setup_ui() connect(&_session, SIGNAL(capture_state_changed(int)), this, SLOT(capture_state_changed(int))); connect(&_session, SIGNAL(device_attach()), this, - SLOT(device_attach())); + SLOT(device_attach()), Qt::QueuedConnection); connect(&_session, SIGNAL(device_detach()), this, - SLOT(device_detach())); + SLOT(device_detach()), Qt::QueuedConnection); connect(&_session, SIGNAL(test_data_error()), this, SLOT(test_data_error())); connect(&_session, SIGNAL(malloc_error()), this, @@ -258,8 +259,6 @@ void MainWindow::setup_ui() SLOT(cursor_update())); connect(_view, SIGNAL(cursor_moved()), _measure_widget, SLOT(cursor_moved())); - connect(_view, SIGNAL(mode_changed()), this, - SLOT(update_device_list())); // event filter _view->installEventFilter(this); @@ -320,11 +319,19 @@ void MainWindow::update_device_list() if (!selected_device->name().contains("virtual")) { _file_bar->set_settings_en(true); _logo_bar->dsl_connected(true); - QString ses_name = DS_RES_PATH + - selected_device->name() + - QString::number(selected_device->dev_inst()->mode) + - ".dsc"; - load_session(ses_name); + #if QT_VERSION >= 0x050400 + QDir dir(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation)); + #else + QDir dir(QStandardPaths::writableLocation(QStandardPaths::DataLocation)); + #endif + if (dir.exists()) { + QString str = dir.absolutePath() + "/"; + QString ses_name = str + + selected_device->name() + + QString::number(selected_device->dev_inst()->mode) + + ".dsc"; + load_session(ses_name); + } } else { _file_bar->set_settings_en(false); _logo_bar->dsl_connected(false); @@ -439,7 +446,13 @@ void MainWindow::run_stop() void MainWindow::instant_stop() { - +#ifdef TEST_MODE + if (!test_timer_linked) { + connect(&test_timer, SIGNAL(timeout()), + this, SLOT(instant_stop())); + test_timer_linked = true; + } +#endif switch(_session.get_capture_state()) { case SigSession::Init: case SigSession::Stopped: @@ -506,12 +519,24 @@ void MainWindow::capture_state_changed(int state) _trig_bar->enable_toggle(state != SigSession::Running); _measure_dock->widget()->setEnabled(state != SigSession::Running); } + +#ifdef TEST_MODE + if (state == SigSession::Stopped) { + test_timer.start(qrand()%1000); + } +#endif } void MainWindow::session_save() { - QDir dir(DS_RES_PATH); - if (dir.exists()) { + QDir dir; + #if QT_VERSION >= 0x050400 + QString path = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation); + #else + QString path = QStandardPaths::writableLocation(QStandardPaths::DataLocation); + #endif + if(dir.mkpath(path)) { + dir.cd(path); QString driver_name = _session.get_device()->name(); QString mode_name = QString::number(_session.get_device()->dev_inst()->mode); QString file_name = dir.absolutePath() + "/" + driver_name + mode_name + ".dsc"; @@ -585,7 +610,8 @@ void MainWindow::on_screenShot() QSettings settings; QPixmap pixmap; QDesktopWidget *desktop = QApplication::desktop(); - pixmap = QPixmap::grabWindow(desktop->winId(), pos().x(), pos().y(), frameGeometry().width(), frameGeometry().height()); + pixmap = QPixmap::grabWindow(desktop->winId(), parentWidget()->pos().x(), parentWidget()->pos().y(), + parentWidget()->frameGeometry().width(), parentWidget()->frameGeometry().height()); QString format = "png"; QString fileName = QFileDialog::getSaveFileName(this, @@ -625,12 +651,13 @@ bool MainWindow::load_session(QString name) { QFile sessionFile(name); if (!sessionFile.open(QIODevice::ReadOnly)) { - dialogs::DSMessageBox msg(this); - msg.mBox()->setText(tr("File Error")); - msg.mBox()->setInformativeText(tr("Couldn't open session file!")); - msg.mBox()->setStandardButtons(QMessageBox::Ok); - msg.mBox()->setIcon(QMessageBox::Warning); - msg.exec(); +// dialogs::DSMessageBox msg(this); +// msg.mBox()->setText(tr("File Error")); +// msg.mBox()->setInformativeText(tr("Couldn't open session file!")); +// msg.mBox()->setStandardButtons(QMessageBox::Ok); +// msg.mBox()->setIcon(QMessageBox::Warning); +// msg.exec(); + qDebug("Warning: Couldn't open session file!"); return false; } @@ -740,12 +767,13 @@ bool MainWindow::store_session(QString name) { QFile sessionFile(name); if (!sessionFile.open(QIODevice::WriteOnly | QIODevice::Text)) { - dialogs::DSMessageBox msg(this); - msg.mBox()->setText(tr("File Error")); - msg.mBox()->setInformativeText(tr("Couldn't open session file to write!")); - msg.mBox()->setStandardButtons(QMessageBox::Ok); - msg.mBox()->setIcon(QMessageBox::Warning); - msg.exec(); +// dialogs::DSMessageBox msg(this); +// msg.mBox()->setText(tr("File Error")); +// msg.mBox()->setInformativeText(tr("Couldn't open session file to write!")); +// msg.mBox()->setStandardButtons(QMessageBox::Ok); +// msg.mBox()->setIcon(QMessageBox::Warning); +// msg.exec(); + qDebug("Warning: Couldn't open session file to write!"); return false; } QTextStream outStream(&sessionFile); diff --git a/DSView/pv/prop/enum.cpp b/DSView/pv/prop/enum.cpp index 2aee897f..af4a8c9f 100644 --- a/DSView/pv/prop/enum.cpp +++ b/DSView/pv/prop/enum.cpp @@ -23,6 +23,7 @@ #include #include +#include #include "enum.h" @@ -60,12 +61,14 @@ QWidget* Enum::get_widget(QWidget *parent, bool auto_commit) return NULL; _selector = new QComboBox(parent); + _selector->setSizeAdjustPolicy(QComboBox::AdjustToContents); for (unsigned int i = 0; i < _values.size(); i++) { const pair &v = _values[i]; _selector->addItem(v.second, qVariantFromValue((void*)v.first)); if (value && g_variant_compare(v.first, value) == 0) _selector->setCurrentIndex(i); } + _selector->view()->setMinimumWidth(_selector->width()+30); g_variant_unref(value); diff --git a/DSView/pv/sigsession.cpp b/DSView/pv/sigsession.cpp index a3ae90d3..d85341fa 100644 --- a/DSView/pv/sigsession.cpp +++ b/DSView/pv/sigsession.cpp @@ -120,6 +120,11 @@ SigSession::SigSession(DeviceManager &device_manager) : connect(&_view_timer, SIGNAL(timeout()), this, SLOT(check_update())); connect(&_refresh_timer, SIGNAL(timeout()), this, SLOT(data_unlock())); + + #ifdef TEST_MODE + _test_timer.setSingleShot(true); + connect(&_test_timer, SIGNAL(timeout()), this, SLOT(stop_capture())); + #endif } SigSession::~SigSession() @@ -509,10 +514,12 @@ void SigSession::capture_init() _cur_samplerate = _dev_inst->get_sample_rate(); _cur_samplelimits = _dev_inst->get_sample_limit(); _data_updated = false; - if (_dev_inst->dev_inst()->mode == DSO) { + if (_dev_inst->dev_inst()->mode != LOGIC) _view_timer.start(ViewTime); - _noData_cnt = 0; - } + else + _view_timer.stop(); + _noData_cnt = 0; + data_unlock(); // Init and Set sample rate for all SignalData // Logic/Analog/Dso @@ -595,7 +602,8 @@ void SigSession::start_capture(bool instant, void SigSession::stop_capture() { _instant = false; - _view_timer.stop(); + //_data_lock = true; + //_view_timer.stop(); #ifdef ENABLE_DECODE for (vector< boost::shared_ptr >::iterator i = _decode_traces.begin(); @@ -608,9 +616,9 @@ void SigSession::stop_capture() sr_session_stop(); // Check that sampling stopped - if (_sampling_thread.get()) - _sampling_thread->join(); - _sampling_thread.reset(); + if (_sampling_thread.get()) + _sampling_thread->join(); + _sampling_thread.reset(); } vector< boost::shared_ptr > SigSession::get_signals() @@ -710,22 +718,30 @@ void SigSession::sample_thread_proc(boost::shared_ptr dev_inst, dev_inst->run(); + set_capture_state(Stopped); + // Confirm that SR_DF_END was received assert(_cur_logic_snapshot->last_ended()); assert(_cur_dso_snapshot->last_ended()); assert(_cur_analog_snapshot->last_ended()); - set_capture_state(Stopped); } void SigSession::check_update() { + if (_capture_state != Running) + return; + if (_data_updated) { data_updated(); _data_updated = false; _noData_cnt = 0; + #ifdef TEST_MODE + if (!_test_timer.isActive()) + _test_timer.start(qrand()%5000); + #endif } else { if (++_noData_cnt >= (WaitShowTime/ViewTime)) - show_wait_trigger(); + nodata_timeout(); } } @@ -960,10 +976,9 @@ void SigSession::refresh(int holdtime) { boost::lock_guard lock(_data_mutex); - if (!_dev_inst->name().contains("virtual")) { - _data_lock = true; - _refresh_timer.start(holdtime); - } + _data_lock = true; + _refresh_timer.start(holdtime); + if (_logic_data) { _logic_data->init(); //_cur_logic_snapshot.reset(); @@ -1004,6 +1019,7 @@ bool SigSession::get_data_lock() void SigSession::feed_in_header(const sr_dev_inst *sdi) { + (void)sdi; _trigger_pos = 0; _trigger_time = QDateTime::currentDateTime(); const int64_t secs = -cur_sampletime(); @@ -1100,6 +1116,7 @@ void SigSession::feed_in_logic(const sr_datafeed_logic &logic) emit receive_data(logic.length/logic.unitsize); data_received(); //data_updated(); + _data_updated = true; } void SigSession::feed_in_dso(const sr_datafeed_dso &dso) @@ -1141,6 +1158,7 @@ void SigSession::feed_in_dso(const sr_datafeed_dso &dso) m->get_math_stack()->calc_fft(); } + _trigger_flag = dso.trig_flag; receive_data(dso.num_samples); //data_updated(); _data_updated = true; @@ -1537,7 +1555,7 @@ void SigSession::mathTraces_rebuild() has_dso_signal = true; // check already have std::vector< boost::shared_ptr >::iterator iter = _math_traces.begin(); - for(int i = 0; i < _math_traces.size(); i++, iter++) + for(unsigned int i = 0; i < _math_traces.size(); i++, iter++) if ((*iter)->get_index() == dsoSig->get_index()) break; // if not, rebuild @@ -1573,4 +1591,19 @@ uint64_t SigSession::get_trigger_pos() const return _trigger_pos; } +bool SigSession::trigd() const +{ + return _trigger_flag; +} + +void SigSession::nodata_timeout() +{ + GVariant *gvar = _dev_inst->get_config(NULL, NULL, SR_CONF_TRIGGER_SOURCE); + if (gvar == NULL) + return; + if (g_variant_get_byte(gvar) != DSO_TRIGGER_AUTO) { + show_wait_trigger(); + } +} + } // namespace pv diff --git a/DSView/pv/sigsession.h b/DSView/pv/sigsession.h index fe450369..569ae288 100644 --- a/DSView/pv/sigsession.h +++ b/DSView/pv/sigsession.h @@ -97,7 +97,7 @@ private: public: static const int ViewTime = 50; - static const int WaitShowTime = 1000; + static const int WaitShowTime = 500; public: enum capture_state { @@ -142,7 +142,6 @@ public: void start_capture(bool instant, boost::function error_handler); void capture_init(); - void stop_capture(); std::set< boost::shared_ptr > get_data() const; @@ -192,9 +191,10 @@ public: bool get_instant(); bool get_data_lock(); - void mathTraces_rebuild(); + bool trigd() const; + private: void set_capture_state(capture_state state); @@ -282,8 +282,13 @@ private: bool _data_lock; bool _data_updated; + #ifdef TEST_MODE + QTimer _test_timer; + #endif + QDateTime _trigger_time; uint64_t _trigger_pos; + bool _trigger_flag; signals: void capture_state_changed(int state); @@ -331,11 +336,13 @@ signals: public slots: void reload(); void refresh(int holdtime); + void stop_capture(); private slots: void cancelSaveFile(); void data_unlock(); void check_update(); + void nodata_timeout(); private: // TODO: This should not be necessary. Multiple concurrent diff --git a/DSView/pv/toolbars/filebar.cpp b/DSView/pv/toolbars/filebar.cpp index 89e5fd6f..e99929fd 100644 --- a/DSView/pv/toolbars/filebar.cpp +++ b/DSView/pv/toolbars/filebar.cpp @@ -119,6 +119,9 @@ FileBar::FileBar(SigSession &session, QWidget *parent) : _menu->addAction(_action_capture); _file_button.setMenu(_menu); addWidget(&_file_button); + + _screenshot_timer.setSingleShot(true); + connect(&_screenshot_timer, SIGNAL(timeout()), this, SIGNAL(on_screenShot())); } void FileBar::on_actionOpen_triggered() @@ -277,7 +280,9 @@ void FileBar::on_actionStore_triggered() void FileBar::on_actionCapture_triggered() { - on_screenShot(); + _file_button.close(); + QCoreApplication::sendPostedEvents(); + _screenshot_timer.start(100); } void FileBar::enable_toggle(bool enable) diff --git a/DSView/pv/toolbars/filebar.h b/DSView/pv/toolbars/filebar.h index a69f3147..ea99ac66 100644 --- a/DSView/pv/toolbars/filebar.h +++ b/DSView/pv/toolbars/filebar.h @@ -85,6 +85,8 @@ private: QAction *_action_export; QAction *_action_capture; + QTimer _screenshot_timer; + }; } // namespace toolbars diff --git a/DSView/pv/toolbars/samplingbar.cpp b/DSView/pv/toolbars/samplingbar.cpp index 94eb9801..06b324c7 100644 --- a/DSView/pv/toolbars/samplingbar.cpp +++ b/DSView/pv/toolbars/samplingbar.cpp @@ -105,7 +105,7 @@ SamplingBar::SamplingBar(SigSession &session, QWidget *parent) : connect(&_configure_button, SIGNAL(clicked()), this, SLOT(on_configure())); connect(&_run_stop_button, SIGNAL(clicked()), - this, SLOT(on_run_stop())); + this, SLOT(on_run_stop()), Qt::DirectConnection); connect(&_instant_button, SIGNAL(clicked()), this, SLOT(on_instant_stop())); @@ -268,7 +268,13 @@ void SamplingBar::zero_adj() run_stop(); pv::dialogs::WaitingDialog wait(this, get_selected_device()); - wait.start(); + if (wait.start() ==QDialog::Rejected) { + BOOST_FOREACH(const boost::shared_ptr s, _session.get_signals()) + { + if (dsoSig = dynamic_pointer_cast(s)) + dsoSig->commit_settings(); + } + } if (_session.get_capture_state() == pv::SigSession::Running) run_stop(); @@ -327,15 +333,13 @@ void SamplingBar::set_sampling(bool sampling) } if (!sampling) { - g_usleep(100000); - _run_stop_button.setEnabled(true); - _instant_button.setEnabled(true); + enable_run_stop(true); + enable_instant(true); } else { - g_usleep(100000); if (_instant) - _instant_button.setEnabled(true); + enable_instant(true); else - _run_stop_button.setEnabled(true); + enable_run_stop(true); } _configure_button.setEnabled(!sampling); @@ -452,7 +456,6 @@ void SamplingBar::update_sample_rate_selector_value() void SamplingBar::commit_sample_rate() { uint64_t sample_rate = 0; - uint64_t last_sample_rate = 0; if (_updating_sample_rate) return; @@ -468,15 +471,9 @@ void SamplingBar::commit_sample_rate() if (sample_rate == 0) return; - // Get last samplerate - last_sample_rate = get_selected_device()->get_sample_rate(); - - //if (last_sample_rate != sample_rate) { - // Set the samplerate - get_selected_device()->set_config(NULL, NULL, - SR_CONF_SAMPLERATE, - g_variant_new_uint64(sample_rate)); - //} + get_selected_device()->set_config(NULL, NULL, + SR_CONF_SAMPLERATE, + g_variant_new_uint64(sample_rate)); _updating_sample_rate = false; } diff --git a/DSView/pv/toolbars/titlebar.cpp b/DSView/pv/toolbars/titlebar.cpp index 64ccd572..26237fcf 100644 --- a/DSView/pv/toolbars/titlebar.cpp +++ b/DSView/pv/toolbars/titlebar.cpp @@ -39,10 +39,10 @@ namespace pv { namespace toolbars { TitleBar::TitleBar(bool top, QWidget *parent, bool hasClose) : + QWidget(parent), _moving(false), _isTop(top), - _hasClose(hasClose), - QWidget(parent) + _hasClose(hasClose) { setObjectName("TitleBar"); setFixedHeight(28); diff --git a/DSView/pv/view/decodetrace.cpp b/DSView/pv/view/decodetrace.cpp index ab411632..7a9e72ba 100644 --- a/DSView/pv/view/decodetrace.cpp +++ b/DSView/pv/view/decodetrace.cpp @@ -281,9 +281,6 @@ void DecodeTrace::paint_mid(QPainter &p, int left, int right) assert(_decoder_stack); - const double decode_startX = _decode_start/samples_per_pixel - (_view->offset() / _view->scale()); - const double decode_endX = _decode_end/samples_per_pixel - (_view->offset() / _view->scale()); - BOOST_FOREACH(boost::shared_ptr dec, _decoder_stack->stack()) { if (dec->shown()) { @@ -335,6 +332,8 @@ void DecodeTrace::paint_fore(QPainter &p, int left, int right) { using namespace pv::data::decode; + (void)p; + (void)left; (void)right; } @@ -504,6 +503,7 @@ void DecodeTrace::draw_nodetail(QPainter &p, int h, int left, int right, int y, size_t base_colour) const { + (void)base_colour; const QRectF nodetail_rect(left, y - h/2 + 0.5, right - left, h); QString info = tr("Zoom in For Detials"); int info_left = nodetail_rect.center().x() - p.boundingRect(QRectF(), 0, info).width(); diff --git a/DSView/pv/view/devmode.cpp b/DSView/pv/view/devmode.cpp index f28554aa..54d52ffb 100644 --- a/DSView/pv/view/devmode.cpp +++ b/DSView/pv/view/devmode.cpp @@ -107,6 +107,9 @@ void DevMode::on_mode_change() const boost::shared_ptr dev_inst = _session.get_device(); assert(dev_inst); QPushButton *button = qobject_cast(sender()); + button->setChecked(true); + if (dev_inst->dev_inst()->mode == _mode_button_list[button]->mode) + return; for(std::map::const_iterator i = _mode_button_list.begin(); i != _mode_button_list.end(); i++) { diff --git a/DSView/pv/view/dsldial.cpp b/DSView/pv/view/dsldial.cpp index 3d0b1300..6153964b 100644 --- a/DSView/pv/view/dsldial.cpp +++ b/DSView/pv/view/dsldial.cpp @@ -21,7 +21,8 @@ #include "dsldial.h" -#include +#include +#include namespace pv { namespace view { diff --git a/DSView/pv/view/dsosignal.cpp b/DSView/pv/view/dsosignal.cpp index 470f6965..10b0a128 100644 --- a/DSView/pv/view/dsosignal.cpp +++ b/DSView/pv/view/dsosignal.cpp @@ -120,8 +120,7 @@ DsoSignal::DsoSignal(boost::shared_ptr dev_inst, _hover_point(QPointF(0, 0)), _hover_value(0), _ms_gear_hover(false), - _ms_show_hover(false), - _ms_show(false) + _ms_show_hover(false) { QVector vValue; QVector vUnit; @@ -142,12 +141,6 @@ DsoSignal::DsoSignal(boost::shared_ptr dev_inst, _colour = SignalColours[probe->index % countof(SignalColours)]; - for (int i = DSO_MS_BEGIN; i < DSO_MS_END; i++) - _ms_en[i] = false; - _ms_en[DSO_MS_FREQ] = true; - _ms_en[DSO_MS_VMAX] = true; - _ms_en[DSO_MS_VMIN] = true; - load_settings(); } @@ -194,6 +187,10 @@ void DsoSignal::set_enable(bool enable) get_index() == 0) return; _view->session().refresh(INT_MAX); + /* + * avoid race condition for en_ch_num + * data lock need to lock usb event + */ _dev_inst->set_config(_probe, NULL, SR_CONF_DATALOCK, g_variant_new_boolean(true)); set_vDialActive(false); @@ -230,7 +227,9 @@ void DsoSignal::set_enable(bool enable) _dev_inst->set_config(_probe, NULL, SR_CONF_DATALOCK, g_variant_new_boolean(false)); - _view->session().refresh(800); + _view->session().refresh(RefreshLong); + _view->set_update(_viewport, true); + _view->update(); } bool DsoSignal::get_vDialActive() const @@ -247,6 +246,8 @@ void DsoSignal::set_vDialActive(bool active) bool DsoSignal::go_vDialPre() { if (enabled() && !_vDial->isMin()) { + if (_view->session().get_capture_state() == SigSession::Running) + _view->session().refresh(RefreshShort); const double pre_vdiv = _vDial->get_value(); _vDial->set_sel(_vDial->get_sel() - 1); _dev_inst->set_config(_probe, NULL, SR_CONF_VDIV, @@ -267,6 +268,8 @@ bool DsoSignal::go_vDialPre() bool DsoSignal::go_vDialNext() { if (enabled() && !_vDial->isMax()) { + if (_view->session().get_capture_state() == SigSession::Running) + _view->session().refresh(RefreshShort); const double pre_vdiv = _vDial->get_value(); _vDial->set_sel(_vDial->get_sel() + 1); _dev_inst->set_config(_probe, NULL, SR_CONF_VDIV, @@ -307,7 +310,7 @@ bool DsoSignal::go_hDialPre(bool setted) } else if ((_view->session().get_capture_state() == SigSession::Running || _data->get_snapshots().empty()) && !_view->session().get_instant()) { - _view->session().refresh(100); + _view->session().refresh(RefreshShort); _hDial->set_sel(_hDial->get_sel() - 1); if (!setted) { @@ -349,7 +352,7 @@ bool DsoSignal::go_hDialCur() if (ch_num == 0) return false; - _view->session().refresh(100); + _view->session().refresh(RefreshShort); uint64_t sample_limit = _view->session().get_device()->get_sample_limit(); GVariant* gvar; uint64_t max_sample_rate; @@ -383,7 +386,7 @@ bool DsoSignal::go_hDialNext(bool setted) } else if ((_view->session().get_capture_state() == SigSession::Running || _data->get_snapshots().empty()) && !_view->session().get_instant()) { - _view->session().refresh(100); + _view->session().refresh(RefreshShort); _hDial->set_sel(_hDial->get_sel() + 1); if (!setted) { @@ -485,8 +488,8 @@ bool DsoSignal::load_settings() _vDial->set_value(vdiv); _vDial->set_factor(vfactor); - _dev_inst->set_config(_probe, NULL, SR_CONF_VDIV, - g_variant_new_uint64(_vDial->get_value())); +// _dev_inst->set_config(_probe, NULL, SR_CONF_VDIV, +// g_variant_new_uint64(_vDial->get_value())); // -- coupling gvar = _dev_inst->get_config(_probe, NULL, SR_CONF_COUPLING); @@ -498,8 +501,8 @@ bool DsoSignal::load_settings() return false; } - _dev_inst->set_config(_probe, NULL, SR_CONF_COUPLING, - g_variant_new_byte(_acCoupling)); +// _dev_inst->set_config(_probe, NULL, SR_CONF_COUPLING, +// g_variant_new_byte(_acCoupling)); // -- vpos double vpos; @@ -512,7 +515,10 @@ bool DsoSignal::load_settings() return false; } _zero_vrate = min(max((0.5 - vpos / (_vDial->get_value() * DS_CONF_DSO_VDIVS)), 0.0), 1.0); - _zero_value = _zero_vrate * ((1 << _bits) - 1); + if (_dev_inst->name() == "DSCope") + _zero_value = _zero_vrate * ((1 << _bits) - 1); + else + _zero_value = 0x80; // -- trig_value gvar = _dev_inst->get_config(_probe, NULL, SR_CONF_TRIGGER_VALUE); @@ -602,7 +608,10 @@ int DsoSignal::get_trig_vpos() const double DsoSignal::get_trig_vrate() const { - return _trig_value * 1.0 / ((1 << _bits) - 1.0); + if (_dev_inst->name() == "DSLogic") + return (_trig_value - (1 << (_bits - 1)))* 1.0 / ((1 << _bits) - 1.0) + _zero_vrate; + else + return _trig_value * 1.0 / ((1 << _bits) - 1.0); } void DsoSignal::set_trig_vpos(int pos, bool delta_change) @@ -671,7 +680,10 @@ void DsoSignal::set_zero_vpos(int pos) void DsoSignal::set_zero_vrate(double rate) { _zero_vrate = rate; - _zero_value = rate * ((1 << _bits) - 1); + if (_dev_inst->name() == "DSCope") + _zero_value = _zero_vrate * ((1 << _bits) - 1); + else + _zero_value = 0x80; update_offset(); } @@ -715,13 +727,13 @@ uint64_t DsoSignal::get_factor() void DsoSignal::set_ms_show(bool show) { - _ms_show = show; + _probe->ms_show = show; _view->set_update(_viewport, true); } bool DsoSignal::get_ms_show() const { - return _ms_show; + return _probe->ms_show; } bool DsoSignal::get_ms_show_hover() const @@ -739,7 +751,7 @@ void DsoSignal::set_ms_en(int index, bool en) assert(index > DSO_MS_BEGIN); assert(index < DSO_MS_END); - _ms_en[index] = en; + _probe->ms_en[index] = en; } bool DsoSignal::get_ms_en(int index) const @@ -747,7 +759,7 @@ bool DsoSignal::get_ms_en(int index) const assert(index > DSO_MS_BEGIN); assert(index < DSO_MS_END); - return _ms_en[index]; + return _probe->ms_en[index]; } QString DsoSignal::get_ms_string(int index) const @@ -861,8 +873,6 @@ void DsoSignal::paint_mid(QPainter &p, int left, int right) assert(scale > 0); const double offset = _view->offset(); - paint_measure(p); - const deque< boost::shared_ptr > &snapshots = _data->get_snapshots(); if (snapshots.empty()) @@ -949,6 +959,9 @@ void DsoSignal::paint_fore(QPainter &p, int left, int right) // Paint the text p.setPen(Qt::white); p.drawText(label_rect, Qt::AlignCenter | Qt::AlignVCenter, "T"); + + // Paint measure + paint_measure(p); } } @@ -986,7 +999,7 @@ void DsoSignal::paint_trace(QPainter &p, float zeroP = _zero_vrate * get_view_rect().height() + top;; if (_dev_inst->name() == "DSCope" && _view->session().get_capture_state() == SigSession::Running) - _zero_value = _zero_vrate * ((1 << _bits) - 1); + _zero_value = _zero_vrate * ((1 << _bits) - 1); float x = (start / samples_per_pixel - pixels_offset) + left; double pixels_per_sample = 1.0/samples_per_pixel; uint8_t offset; @@ -1042,7 +1055,7 @@ void DsoSignal::paint_envelope(QPainter &p, float zeroP = _zero_vrate * get_view_rect().height() + top; if (_dev_inst->name() == "DSCope" && _view->session().get_capture_state() == SigSession::Running) - _zero_value = _zero_vrate * ((1 << _bits) - 1); + _zero_value = _zero_vrate * ((1 << _bits) - 1); for(uint64_t sample = 0; sample < e.length-1; sample++) { const float x = ((e.scale * sample + e.start) / samples_per_pixel - pixels_offset) + left; @@ -1212,19 +1225,19 @@ bool DsoSignal::mouse_wheel(int right, const QPoint pt, const int shift) const QRectF hDial_rect = get_rect(DSO_HDIAL, y, right); if (vDial_rect.contains(pt)) { - if (shift > 1.0) + if (shift > 0.5) go_vDialNext(); - else if (shift < -1.0) + else if (shift < -0.5) go_vDialPre(); return true; } else if (hDial_rect.contains(pt)) { boost::shared_ptr dsoSig; BOOST_FOREACH(const boost::shared_ptr t, traces) { if (dsoSig = dynamic_pointer_cast(t)) { - if (shift > 1.0) { + if (shift > 0.5) { dsoSig->go_hDialNext(setted); setted = true; - } else if (shift < -1.0) { + } else if (shift < -0.5) { dsoSig->go_hDialPre(setted); setted = true; } @@ -1327,7 +1340,7 @@ void DsoSignal::paint_measure(QPainter &p) abs(_period) > 1000 ? QString::number(1000000/_period, 'f', 2) + "kHz" : QString::number(1000/_period, 'f', 2) + "MHz"); _ms_string[DSO_MS_VP2P] = "Vp-p: " + (abs(value_p2p) > 1000 ? QString::number(value_p2p/1000.0, 'f', 2) + "V" : QString::number(value_p2p, 'f', 2) + "mV"); - if (_ms_show && _ms_en[DSO_MS_VRMS]) { + if (_probe->ms_show && _probe->ms_en[DSO_MS_VRMS]) { const deque< boost::shared_ptr > &snapshots = _data->get_snapshots(); if (!snapshots.empty()) { @@ -1339,7 +1352,7 @@ void DsoSignal::paint_measure(QPainter &p) } } - if (_ms_show && _ms_en[DSO_MS_VMEA]) { + if (_probe->ms_show && _probe->ms_en[DSO_MS_VMEA]) { const deque< boost::shared_ptr > &snapshots = _data->get_snapshots(); if (!snapshots.empty()) { @@ -1372,7 +1385,7 @@ void DsoSignal::paint_measure(QPainter &p) p.setBrush(measure_colour); p.drawRoundedRect(_ms_rect[DSO_MS_BEGIN], MS_RectRad, MS_RectRad); const QPixmap gear_pix(":/icons/settings.png"); - const QPixmap show_pix(_ms_show ? ":/icons/shown.png" : ":/icons/hidden.png"); + const QPixmap show_pix(_probe->ms_show ? ":/icons/shown.png" : ":/icons/hidden.png"); if (_ms_gear_hover) { p.setBrush(back_colour); p.drawRoundedRect(_ms_gear_rect, MS_RectRad, MS_RectRad); @@ -1385,11 +1398,11 @@ void DsoSignal::paint_measure(QPainter &p) p.setPen(Qt::white); p.drawText(_ms_rect[DSO_MS_BEGIN], Qt::AlignCenter | Qt::AlignVCenter, "CH"+QString::number(index)); - if (_ms_show) { + if (_probe->ms_show) { p.setBrush(back_colour); int j = DSO_MS_BEGIN+1; for (int i=DSO_MS_BEGIN+1; ims_en[i]) { p.setPen(_colour); p.drawText(_ms_rect[j], Qt::AlignLeft | Qt::AlignVCenter, _ms_string[i]); p.setPen(Qt::NoPen); @@ -1436,7 +1449,7 @@ void DsoSignal::paint_measure(QPainter &p) } } } - _view->update(); + //_view->update(); } void DsoSignal::auto_set() diff --git a/DSView/pv/view/dsosignal.h b/DSView/pv/view/dsosignal.h index dbe6cd3c..790bd61c 100644 --- a/DSView/pv/view/dsosignal.h +++ b/DSView/pv/view/dsosignal.h @@ -63,20 +63,10 @@ private: static const uint8_t DefaultBits = 8; static const int TrigMargin = 16; + static const int RefreshShort = 200; + static const int RefreshLong = 800; public: - enum DSO_MEASURE_TYPE { - DSO_MS_BEGIN = 0, - DSO_MS_FREQ, - DSO_MS_PERD, - DSO_MS_VMAX, - DSO_MS_VMIN, - DSO_MS_VRMS, - DSO_MS_VMEA, - DSO_MS_VP2P, - DSO_MS_END, - }; - enum DsoSetRegions { DSO_NONE = -1, DSO_VDIAL, diff --git a/DSView/pv/view/header.cpp b/DSView/pv/view/header.cpp index b47c2370..6e5e430e 100644 --- a/DSView/pv/view/header.cpp +++ b/DSView/pv/view/header.cpp @@ -257,7 +257,7 @@ void Header::wheelEvent(QWheelEvent *event) const vector< boost::shared_ptr > traces( _view.get_traces(ALL_VIEW)); // Vertical scrolling - double shift = event->delta() / 20.0; + double shift = event->delta() / 80.0; BOOST_FOREACH(const boost::shared_ptr t, traces) if (t->mouse_wheel(width(), event->pos(), shift)) break; diff --git a/DSView/pv/view/mathtrace.cpp b/DSView/pv/view/mathtrace.cpp index 0dad2305..0b6b6f4c 100644 --- a/DSView/pv/view/mathtrace.cpp +++ b/DSView/pv/view/mathtrace.cpp @@ -117,7 +117,7 @@ void MathTrace::set_view_mode(int mode) std::vector MathTrace::get_view_modes_support() { std::vector modes; - for (int i = 0; i < sizeof(FFT_ViewMode)/sizeof(FFT_ViewMode[0]); i++) { + for (unsigned int i = 0; i < sizeof(FFT_ViewMode)/sizeof(FFT_ViewMode[0]); i++) { modes.push_back(FFT_ViewMode[i]); } return modes; @@ -191,7 +191,7 @@ int MathTrace::dbv_range() const std::vector MathTrace::get_dbv_ranges() { std::vector range; - for (int i = 0; i < sizeof(DbvRanges)/sizeof(DbvRanges[0]); i++) { + for (unsigned int i = 0; i < sizeof(DbvRanges)/sizeof(DbvRanges[0]); i++) { range.push_back(DbvRanges[i]); } return range; @@ -231,9 +231,9 @@ bool MathTrace::measure(const QPoint &p) if(samples.empty()) return false; - const int full_size = (_math_stack->get_sample_num()/2+1); + const unsigned int full_size = (_math_stack->get_sample_num()/2); const double view_off = full_size * _offset; - const int view_size = full_size*_scale; + const double view_size = full_size*_scale; const double sample_per_pixels = view_size/window.width(); _hover_index = std::round(p.x() * sample_per_pixels + view_off); @@ -251,7 +251,6 @@ void MathTrace::paint_back(QPainter &p, int left, int right) if(!_view) return; - int i, j; const int height = get_view_rect().height(); const int width = right - left; @@ -277,7 +276,7 @@ void MathTrace::paint_mid(QPainter &p, int left, int right) trace_colour.setAlpha(150); p.setPen(trace_colour); - const int full_size = (_math_stack->get_sample_num()/2+1); + const int full_size = (_math_stack->get_sample_num()/2); const double view_off = full_size * _offset; const int view_start = floor(view_off); const int view_size = full_size*_scale; @@ -289,8 +288,8 @@ void MathTrace::paint_mid(QPainter &p, int left, int right) const double width = right - left; const double pixels_per_sample = width/view_size; - double vdiv; - double vfactor; + double vdiv = 0; + double vfactor = 0; BOOST_FOREACH(const boost::shared_ptr s, _session.get_signals()) { boost::shared_ptr dsoSig; if (dsoSig = dynamic_pointer_cast(s)) { @@ -349,6 +348,8 @@ void MathTrace::paint_fore(QPainter &p, int left, int right) return; assert(right >= left); + (void)left; + (void)right; const int text_height = p.boundingRect(0, 0, INT_MAX, INT_MAX, AlignLeft | AlignTop, "8").height(); const double width = get_view_rect().width(); @@ -427,7 +428,7 @@ void MathTrace::paint_fore(QPainter &p, int left, int right) const std::vector samples(_math_stack->get_fft_spectrum()); if(samples.empty()) return; - const int full_size = (_math_stack->get_sample_num()/2+1); + const int full_size = (_math_stack->get_sample_num()/2); const double view_off = full_size * _offset; const int view_size = full_size*_scale; const double scale = height / (_vmax - _vmin); diff --git a/DSView/pv/view/view.cpp b/DSView/pv/view/view.cpp index b9d39380..357fdb8e 100644 --- a/DSView/pv/view/view.cpp +++ b/DSView/pv/view/view.cpp @@ -81,7 +81,8 @@ View::View(SigSession &session, pv::toolbars::SamplingBar *sampling_bar, QWidget _preOffset(0), _updating_scroll(false), _show_cursors(false), - _hover_point(-1, -1) + _hover_point(-1, -1), + _dso_auto(true) { setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn); @@ -165,8 +166,6 @@ View::View(SigSession &session, pv::toolbars::SamplingBar *sampling_bar, QWidget connect(&_session, SIGNAL(show_wait_trigger()), _time_viewport, SLOT(show_wait_trigger())); -// connect(_devmode, SIGNAL(mode_changed()), -// this, SIGNAL(mode_changed())); connect(_devmode, SIGNAL(mode_changed()), parent, SLOT(update_device_list()), Qt::DirectConnection); @@ -629,7 +628,7 @@ void View::signals_changed() // Find the decoder in the stack std::list< Viewport *>::iterator iter = _viewport_list.begin(); - for(int i = 0; i < _viewport_list.size(); i++, iter++) + for(unsigned int i = 0; i < _viewport_list.size(); i++, iter++) if ((*iter) == _fft_viewport) break; // Delete the element @@ -803,6 +802,7 @@ void View::h_scroll_value_changed(int value) void View::v_scroll_value_changed(int value) { + (void)value; _header->update(); viewport_update(); } diff --git a/DSView/pv/view/view.h b/DSView/pv/view/view.h index 9c73212e..5ba901e9 100644 --- a/DSView/pv/view/view.h +++ b/DSView/pv/view/view.h @@ -202,8 +202,6 @@ signals: void cursor_moved(); - void mode_changed(); - void measure_updated(); private: @@ -305,6 +303,7 @@ private: QPoint _hover_point; dialogs::Calibration *_cali; + bool _dso_auto; }; } // namespace view diff --git a/DSView/pv/view/viewport.cpp b/DSView/pv/view/viewport.cpp index 9185c5c1..a1e47c57 100644 --- a/DSView/pv/view/viewport.cpp +++ b/DSView/pv/view/viewport.cpp @@ -238,14 +238,36 @@ void Viewport::paintSignals(QPainter &p) //plot measure arrow paintMeasure(p); - //plot waiting trigger - if (_waiting_trig > 0) { + //plot trigger information + if (_view.session().get_device()->dev_inst()->mode == DSO && + _view.session().get_capture_state() == SigSession::Running) { + uint8_t type; + bool stream = false; + QString type_str=""; + GVariant *gvar = _view.session().get_device()->get_config(NULL, NULL, SR_CONF_STREAM); + if (gvar != NULL) { + stream = g_variant_get_boolean(gvar); + g_variant_unref(gvar); + } + gvar = _view.session().get_device()->get_config(NULL, NULL, SR_CONF_TRIGGER_SOURCE); + if (gvar != NULL) { + type = g_variant_get_byte(gvar); + g_variant_unref(gvar); + if (type == DSO_TRIGGER_AUTO && stream) { + type_str = "Auto(Roll)"; + } else if (type == DSO_TRIGGER_AUTO && !_view.session().trigd()) { + type_str = "Auto"; + } else if (_waiting_trig > 0) { + type_str = "Waiting Trig"; + for (int i = 1; i < _waiting_trig; i++) + if (i % (WaitLoopTime / SigSession::ViewTime) == 0) + type_str += "."; + } else { + type_str = "Trig'd"; + } + } p.setPen(Trace::DARK_FORE); - QString text = "Waiting Trig"; - for (int i = 1; i < _waiting_trig; i++) - if (i % (WaitLoopTime / SigSession::ViewTime) == 0) - text += "."; - p.drawText(_view.get_view_rect(), Qt::AlignLeft | Qt::AlignTop, text); + p.drawText(_view.get_view_rect(), Qt::AlignLeft | Qt::AlignTop, type_str); } } } @@ -255,6 +277,7 @@ void Viewport::paintProgress(QPainter &p) using pv::view::Signal; const uint64_t _total_sample_len = _view.session().cur_samplelimits(); + double progress = -(_total_receive_len * 1.0 / _total_sample_len * 360 * 16); int captured_progress = 0; @@ -338,10 +361,12 @@ void Viewport::paintProgress(QPainter &p) sr_status status; if (sr_status_get(_view.session().get_device()->dev_inst(), &status, SR_STATUS_TRIG_BEGIN, SR_STATUS_TRIG_END) == SR_OK){ const bool triggred = status.trig_hit & 0x01; - const uint32_t captured_cnt = (status.captured_cnt0 + + uint32_t captured_cnt = (status.captured_cnt0 + (status.captured_cnt1 << 8) + (status.captured_cnt2 << 16) + (status.captured_cnt3 << 24)); + if (_view.session().get_device()->dev_inst()->mode == DSO) + captured_cnt = captured_cnt * _view.session().get_signals().size() / _view.session().get_ch_num(SR_CHANNEL_DSO); if (triggred) captured_progress = (_total_sample_len - captured_cnt) * 100.0 / _total_sample_len; else @@ -924,7 +949,7 @@ void Viewport::measure() BOOST_FOREACH(const boost::shared_ptr t, _view.session().get_math_signals()) { assert(t); if(t->enabled()) { - t->measure(_view.hover_point()); + t->measure(_mouse_point); } } } @@ -1279,7 +1304,12 @@ void Viewport::on_drag_timer() _drag_strength /= DragDamping; if (_drag_strength != 0) _drag_timer.start(DragTimerInterval); - } else if (_action_type == NO_ACTION){ + } else if (offset == _view.get_max_offset() || + offset == _view.get_min_offset()) { + _drag_strength = 0; + _drag_timer.stop(); + _action_type = NO_ACTION; + }else if (_action_type == NO_ACTION){ _drag_strength = 0; _drag_timer.stop(); } diff --git a/DSView/pv/widgets/border.cpp b/DSView/pv/widgets/border.cpp index e11d6d6f..3196d188 100644 --- a/DSView/pv/widgets/border.cpp +++ b/DSView/pv/widgets/border.cpp @@ -30,8 +30,8 @@ namespace pv { namespace widgets { Border::Border(int type, QWidget *parent) : - _type(type), - QWidget(parent) + QWidget(parent), + _type(type) { setAttribute(Qt::WA_TranslucentBackground); setMouseTracking(true); diff --git a/DSView/res/DSCope.bin b/DSView/res/DSCope.bin old mode 100644 new mode 100755 index 505dbb74bba85c0d0223dc7b7ba13afc34def2b1..e56e9a428676b98dcfe53c88cee0975d24bef7fa GIT binary patch literal 341160 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%0A2H{MHGr(~RM6r{czU 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 Az5oCK literal 340884 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 z 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 Q?pNRV7EJ!=1?38Q3IG5A diff --git a/DSView/res/DSCope.fw b/DSView/res/DSCope.fw old mode 100644 new mode 100755 index 28b5af07e4a1e947f9d91610ac0549462f6e4a28..030486b2600ba28a5d16913c13d9952b96123103 GIT binary patch delta 2978 zcmZuzdr(x@89(>#ds$%jF1x+}0he*Gu`;!cO`RrdKpu*KSw%=h&=fRLSX=ICiCXsN zQf8tcd&3R?)TYx8eUBb_pEYETa>U8WfNty!Tt`)X_VcRy_9{FU6ehPvncy1x9c2X{j>wW zg&J&>9h4t+Y^AcDvXe3$sP86g{Q|!e=KTF}vBktPe|%fGRfbxY9bzQE)WzVzY(@Th z*nS$Zmci@aeDO6NOetORZKlYw0^za-c`SD!#!KNQu5!>Y^!EnhJtq%8pkc&XyE13|TSZ6%1$l8!kZL z`TtE=!Ni&`a<=z4cD0na1Ot8m^FoY1O(L3W^-Re-%`cU-Nr`Vrhn`Y_2??6vRw~#9 zQ%HD7qC@@Z4qD`Ym{(@RQc_~lNcq$&F*(EX{k6|=w&&t4L0mJXXti3oR-sjDRoW)J z*{)S%F`si@kPlk2B3p4~5LZp%O$e7W3V3bSs z`RxMFtkFWsOWHP~4g^xs?fvz5N078|6+I^}zI;7((bw?n2=5Bw<|*8Q@J=6YYD8G9 zPud&SQdPeG&q!uFsCi7Y&98u#C^ z$a0?BrGoi$@FxyJEwq2|Xh)FZU=_@Wjw z7Rf>C;r;Z0^DM3CiIgSlQn}u-F8>M&y*Dc*ns`Q??3olNV?P*=jf>+wRLiRJUmf2w zUj|Wr;HYqr3h~Yxbmq$KPMdFYVYyZpuP&+3wo2JZQD@hr@T>AsXFls+B#$`jcfD$i z1UbiTa9$^Etn=C-bQn$C9;&-Dv=y!tmkH|ScZMqJ{I?4$=kAnLB}X>BD<19XS2=(9 zp^$TXVyJu$?8)iEEpxX@YLcgFiFtsS<(9NHu4_`FE4EO4!^Ebw$tTjbT0(YZY@7U_ zwAEw~d9K~w2Cb223LEB>lE&o3^ib&5&^GyfS7EtpBhOOx^jOGsYit|b4PCKhS)Te) z!^e%524$>)wKR@2oNSyf+zy@BCqLOCCEkwRCnM-nIiJG1F3Ty`ihS2b(y}b5{=0Dd z!@DK*^P{@sLce&tCrTXU3u(ELUU9dgwQ6mjyf*UsM7(uqXS{8w36hWzJOmeg7hGJ5 z^$^oSu%T50uE>u8&$RT*n#Ep0P}yB20kno}oR2E=I3agvIA$I8hO zs@#LC5Z)A2zPodJcZ=iZaT6S>NL-vWO_~pyG_fG!YD^oYH0hI-%H*1Lau?}7T>_8n0X?yb0} zn@A_U;!?Reqc9SNL#Oi0syStSvfoA$3HkAd=cL4h%CoEHmGuk#w&UV}p_USVR(aF2 z+Dj^Lc~(2F^43-NmGuvfiG9Wfx(~-j#iO>9OHVBuutk5*e*#Wj3&X1|3kV$4#uO-AkaXaEGnR@Ex+C=iEPDstnIS_1)~FInmt5 zQ&|`$Thr|+aw@~KV0N+|pIquGSsEsW6wFkeR*#Cu$PV&{le+hyr#Rw8v-AhmtzRnZ zs?lzQeu0j>f9fX3c{wlqbL8{>Qh!%?eflq18KbgZAD4xmr>uFN@*(keD5E0WC9{w3 zlC^LNfI&)}iD@F|n0~6Iuvbc)i>2@)H(reJk|3nzU%UptSVT5Y+p7L{`5uPA^Tu%chM@-b`(Odpw(y(ChNBW8~_r; O3a|s500{28iT($h3s@Kc delta 2838 zcmZuzYitzP6`r~KUfZxc_87lymNf_xf<@|-2FYI71q`vl4?-X$HYOpsl_*r5D;d&w zC$qQ-#@G{XHvC9awepV;1+auMFY{RJkQkIID=M{W8`MQwuMG}(8W`aX;O)6H!=sdu z&V2XYbIy0p+{c-Fvhrl5ye;?vLm%WgqN8(%j{L_uI=(Z;iwxXm=pzPRFmTYoiw1sT z;86qn4D8p@ZD26FLLYGc+Q99G!7&4m8+by;?3p_9=L{S&@M{By4Lon)R|bA#;3XZi zOEe53-Uz^-Gz?A~c*Z~@Fn`ME4;XmXKqFB;jI2BH`z>5{<%qP&gxKRd#p?lPQ~oYi z&i1#mcz;2h{MchZ$so)Af86=!k9dD7-b!w;b1hy5mwQPL`rYicL4~r)30ExjsGa#l zo2PAZ4jl{zoQiDup5rge2{E?zt|BLxSV|ae0t~p65$4L6oM?#!?4Wof7C<_b=-|?U zRj%}M=v`#}u8eVvcy?Gg_sKyuL}|( z*a^YEGO>Jh;F~gHi)Wj=@;#ysX@F? z>D$FwPe=F_`MY^e4pta0+mn(LQJrygsXju|EibgR&k~WV!fE_Pmur!l%{^l7UgX-}TA6`0&{S2;T634L;h^Z4OYi1)EYW5 z-qKX}YP^QQwLYp&`~De}_L4vN1uCZ@iPsr zj|?A@`%@*?7U@1{t~|OVrdH^ln{YEj`>F2D_$>y%{Sw~7;CCQ32y>TuQ{hY-I8!f) zS)1%#pW@Hr_>T;!vb8n|tkNJp@QVoMQ58}OXMKNUpltM>oVcxRW;u~3tkh;6R*Np1 zOT-f5+@JYH^IyT|b}S$Q!@Y-VPdPM#2JtR%kplbr7wzK{-axGhQLLTvIzy^-%j*iM zq4;mBnyjS5sjjExW-cnNJ$SD*`H09xn zHm2}wiY`;;tQs*oxSO`zAG!iJSR9B8HEBC3ZQvyw z+!qUP423HvL7yBeT|Rled`0p|6;*dpH94HNAiYCQw8o~&t^rpr8B43PRNLeIHKZba zK22gp-bux?V}TMx1$pK7`$X7 zUdmvHgyCqmrN*c9kjP0AN+uy2be|vTNmNo6~)OJfyQAQ+9MT4|U zdF>%`WZJA3=y6j7cHU%sakA4!Clcl-+XM4AqzGuyY;98-`R)mst;qPA`P_t?BvjcDnLyz;HQHE)&o7`bOjymm-7zv+=AwZ1$zp+iyq}Z)kF8< zo9?;J13eRk=^Ba9;!&erzvB~9;t(FnQTu|v;MccEXeguXmw=5s3SL0eOY~d zbNd#oXb!vG+~X&6p7xLDhPA&-fK?DT%w_gHV=#+-)?hESci>(LYV8EnjgC0H_Pz)O z0TgoLbWD{v$Ihob#jSGUOe}?GxNr`GbA7O+_6c%ND;AVVNugAD%yUSENH19AzBO8~(Uu6T$)4RQtqNZ?9`_yLf!AVAKCng0Tc C=s|Y? diff --git a/DSView/res/DSCope1.def.dsc b/DSView/res/DSCope1.def.dsc old mode 100644 new mode 100755 index 12412f65..852f890f --- a/DSView/res/DSCope1.def.dsc +++ b/DSView/res/DSCope1.def.dsc @@ -1,7 +1,7 @@ -{ +{ "Device": "DSCope", "DeviceMode": 1, - "Horizontal trigger position": "0", + "Horizontal trigger position": "1", "Operation Mode": "Normal", "Sample count": "1048576", "Sample rate": "100000000", @@ -16,7 +16,6 @@ "enabled": true, "index": 0, "name": "0", - "strigger": 0, "trigValue": 0.5, "type": 10001, "vdiv": 1000, @@ -29,7 +28,6 @@ "enabled": true, "index": 1, "name": "1", - "strigger": 0, "trigValue": 0.5, "type": 10001, "vdiv": 1000, diff --git a/DSView/res/DSCope1.dsc b/DSView/res/DSCope1.dsc deleted file mode 100644 index 12412f65..00000000 --- a/DSView/res/DSCope1.dsc +++ /dev/null @@ -1,40 +0,0 @@ -{ - "Device": "DSCope", - "DeviceMode": 1, - "Horizontal trigger position": "0", - "Operation Mode": "Normal", - "Sample count": "1048576", - "Sample rate": "100000000", - "Time base": "10000", - "Trigger hold off": "0", - "Trigger slope": "0", - "Trigger source": "0", - "channel": [ - { - "colour": "#eeb211", - "coupling": 0, - "enabled": true, - "index": 0, - "name": "0", - "strigger": 0, - "trigValue": 0.5, - "type": 10001, - "vdiv": 1000, - "vfactor": 1, - "zeroPos": 0.5 - }, - { - "colour": "#009925", - "coupling": 0, - "enabled": true, - "index": 1, - "name": "1", - "strigger": 0, - "trigValue": 0.5, - "type": 10001, - "vdiv": 1000, - "vfactor": 1, - "zeroPos": 0.5 - } - ] -} diff --git a/DSView/res/DSCope20.bin b/DSView/res/DSCope20.bin new file mode 100755 index 0000000000000000000000000000000000000000..ffd960cbf2f8f0fddbf87d8e0ab0df9d30b5f1c9 GIT binary patch literal 341160 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 literal 0 HcmV?d00001 diff --git a/DSView/res/DSCope20.fw b/DSView/res/DSCope20.fw new file mode 100755 index 0000000000000000000000000000000000000000..5071e7855b6e9ed8b3b5e7c4ea27ade36c039174 GIT binary patch literal 8120 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 literal 0 HcmV?d00001 diff --git a/DSView/res/DSLogic.fw b/DSView/res/DSLogic.fw old mode 100644 new mode 100755 index 440906092f6e3eb188f1f2c9484fadeb2b674624..f32f92a7d6484476f7b1cc144221386724d66a8a GIT binary patch delta 2820 zcmZuzYfw~27Vg{g9)@A&4tH=>6v+@*q9v6S*~(gpC=WrAM@A5g57Y%s6ehdO-6h6y zW-p;C3c`)O?4NA5QpJzNQXy5lY`GdED?hTyK5AmBbd%a2gSj&xM7YU%BNJt2Pv2=Z zRpHj0{!X7)cb`7p(-${h+$gt)zC-lGb|=FtX%r?{-^Vb(@KPGRnGBhqH;eW8Gzwoa zOfk6*2zzn5&oc~pd(#5@WriOz1CF7Ap)rl#4u%;FT?{=81%?iWZid+my$suR3}G)D zz-wX(3qu>jhXb2g-^$R=kWEy$h0R}zzYXx-zL?Zvz}#P63*Rn7Ez6E^vRCfn#52nk z^6ilIBEqIKU)=rjTk%Xv=_1qIIolhaExhkW2XeP%3Dz zRb|tEd2fy!4}<*PK2?q*U?%sKxzrfCJ|)KwL)Qvs@!i1SS{2$ny>;m=SF2th;qmL( zQdER&vHVv}Ja4*&f;9#CB?fH15zF_L)iI_f<+FEd(wxuPt#vD%_Qcxp4L5%I_RSl6 z$Zf+&g4c(6oSEmhEt^8rcO?t!-gv@(nxyUr| z8oC^~ljxz8^vcl4}*Qol# z3#DNU#i0dL`fkQ97fs%m{Ua8bSB= zhwt&`Tc78%fj2&LaL!i>HEaW{OX?*&>vQWReD+!seE$KmHa%~(+Thb08wptpIU_k| zeb!oj0_9#waVyB5tp&NUa#fUQG2e|Q{bmfQoQ0!xN`a==NL;qHoI;+r{lrGkSP;Wu zBZqCfR$WK_x8~&do*<`2x+78~`t(?IOd9KEQmm2%+f&BRpi7GEThdI(UMOrUsL<+S z)x}%1dU-ig)VcL3@kR2HeHHfRkO_O^-WQFc1ZND{$1HGwA{K1Fc?`Xd#-|7CW(Mow z06Pr8SIi75Iu7uO_)YpJ@Q zs>xu+ddE#U-W5$2O&D-S8;NApoBUQ~bO(9Tv5ron!Li@dq%{Y*f~I+;xH&OCHR!)R zxPyG+c(S}ah?#V1)bF@Gx&!K|E1D?FQ{Qho+iYr5Mw_sy`Fzub=Ba`P7`riXZl@f7 zE&4s3K(ERR3U+)(S{y42if+M5%Bgo*iTZUx!-G4;jSC~Xak5W3-912!NmpiGXuqPh zYHjCsoquUO);hR5);72YvfwN^26t)~y_(F&xIW9kfm0UyobN!Q*ZiKDvBRw+ZKhUb zv=y7$W()Qv=Zg2GF0}MZ1Kp=-F7BPGpoLy}K&(RI))M9MP0_Po8Z(X=;D(7wp@<=3 zeAS>y`5BiH=fIhlXtj(;aD~9|3o!f%?!w{I9sMTf9bZS{{Gs=Z{pq+JKZ-jb!KTun z5sMzN=VgD-D?56QH1r&8>}lUQAi-r5lXMjwq(@rX(@lQwd?F_Rr$Y^Lh4adWM4yEw z6Y>MQ8F6|yf$nc=&``LbY)JN5PD}l19o)of(DY;Jm>M(}ey?n}e@g02mke2*C369C zF)P;su|9?(Cu3Ox<^K+GDGyqwYZ;Y%3NasQ{{fLGzYOI6RD+ha4lS8SNIsec zIqnLWm7y8Bncuj6zEl~S1@_J5Cvyi@JX%I_h_0pFy~eH#&4JpE-2&-y7cUJ^MQUD- zt5fPp=@hLXZy=#tf9x&_dC(mD_j2h+|7X=`KSIAo$A33`i|0MO2Yx*LJipQZygh<` z`;~Dj_6U47T1;58nD7C$w<#kMTn=-OE(b51eNf2pOHobYZBxvX^zqBNL*y)9wB!xg z%#{zv41?ugsipNO`G(J5(mBiStxSFL)pZ9u4|N{t43MWhZ*aNf1JA*gxx>;(cXYvT zO46deqlPo}yFm|wA`Lw^8O^S=k6yJHu<$7sz91H%3U;ktSk49h)FB+R zo>_5XO`)Ly`O#*y4jq6*b)r`E3zUbdPzUfq^eZHyc93<^fVTw q3?LVf2S|7h8aGfjkQb;O2pr)-hrJdc8xRd*2C@R#fgreW3;iF)I2W1# delta 2776 zcmZuz3v3j}8J?YcpC8<@#P$5WfQn56M8{9qN#j zyY9tNu#LT8>PS_UD#d9-8;tvqQ%GoWuqrjgl}@y3)u>Vre z>6`DL`5!YokALUPhBF()_P{x$6m0uKWBV^O=04TiMxA{+pV0NP&R^<$Q|DVcf2MOn z=cLY)8as6k#`)Vd=C12JtMi=B^E%(qIj!?;ofkCD-==Z^ zbNT?>v~G~n`Kr$Pz}$@9mUO5uH_K(jcra3bQxfASlHn6Az#fMbLzj|bd{4w<1w~`TgEew#jiKc3fX+{phKo4)XcX6EtOb zsQT~0Trp7@$&^P8uJYVfN8~8Xqhm5@PfH@X=vyr4r9*Jv7_& zOnnL2R!O!eqOxDEk!xkITqkcOJ}aq5L|DPve|eu-;jP45N$L`0D8K_=A_V5< zWph{GanHCeKdz0)xQW+8>JZslsVx=pE1ykj5%DqqvrLLFYuLdWhJ$k={leU6?fWTt zCrW;X=tfIt3(rWt;CxjO!{*9-Mq}hpK{BWfL7EM8K@` zQdQ9SH}JV0@dP2l+?@lU#83nHf;S-+Ewb+TrYohMKO3-jK|OvmZijmOfTUQ8`Lw%I7G~Bacq~QWnCVR;AEZ~!%jlnNPpliYjD|@VT|&ce@u?R1 zEiv8%gNDOlOE?r3!jZ>DBcsA-NHMbBV}iki7TX`Op4B_)X8U(@?lhDjE?Q?_Ui280 zVIk%#tC63L)mPNYJH#Rsl~Rvp$aC}`_7#}R(6oJf^K(Wbz$HWWQ48Fmum#)CA4adD zvB`mFrv`Sw4bGYZxn^ph)|)?B=AE6Ys7s&P`lfI+)GNUix*aT@92=;a1${bMwrzHz zVte|uUs3ldYC4qlX!dzA-WgdazYMWzXfmtL)L@N{G}86iYm`Z}XSe0G%56MT);b%l zXiJYJ2O1^@8tLWimDOu_toJ5I8nP!w8m&sGw8tf#Z2eW6sWm#%icM{&T8G+_Wlb=4 zbNU0wT(3lKDHG_CSZ*9=pQW2~+%8BJoe@u^bfv86_H;$_{IF`b&?_7Z^(%H%%y9+w zM&)*S{|C>X+Bp_$AJ`S!Kkx!9K<-0_k>U+$$$XgUwe;`nw%Akded!+a`(|A{-9EhE z)E*sa$EN)=WiKwIDt2dv_Vfw;p<~K2-0ZGVE{AuYc4?wtWrwc4!l-f709hp_1j2@} z@t{E#O0zyjOg7Frp&Y?s;fOl?JPdz?89I3EP@gGx+I=W}>cIQPJ}vH{|BKr%zRFyJFm$0lWL{H~F@C`rQDv4)S%|_Rydo9X3 z!usUG0R zGJYCfR1EdZqC%+cjzSAW_*CMJ)ZyS1`65J9WY9J{3JalUT;eUOZ$qRjYv!lcFMKur zj~xfhwcY4#oqSoVF9vn3bICb6sn%bAsv81jDHl71=e=@Yo5W~V*FerLzXy5 z0U}E)VM}dp`UzWJ@HA{}$(^YLIWc}#31F?Jw!G4Uh8aBv=BPPv?YF<*P}|^Z@YAZi z*BKN2OWsbXS1&GRs_FL@KMC^di#LM&*Toy{@0_<7F!wvmeL_pPO|Ut8xgy5@`!~45 zR_a<(aD4fZMGk`p`Os!`0PRM5(N5}I(h&Vf%Z~HvyOeTtOHfa#|cEmV)bmS->1%{z<6PfZKot eFy00Xj_^psTnca%072kUhB*mv4FJKpi|D`N;zE!B diff --git a/DSView/res/DSLogic0.def.dsc b/DSView/res/DSLogic0.def.dsc old mode 100644 new mode 100755 index 780b982f..08aea6ec --- a/DSView/res/DSLogic0.def.dsc +++ b/DSView/res/DSLogic0.def.dsc @@ -1,268 +1,270 @@ -{ - "Device": "DSLogic", - "DeviceMode": 0, - "Filter Targets": "None", - "Horizontal trigger position": "0", - "Operation Mode": "Normal", - "Sample count": "16777216", - "Sample rate": "100000000", - "Threshold Level": 1, - "Trigger hold off": "0", - "Trigger slope": "0", - "Trigger source": "0", - "Using Clock Negedge": 0, - "Using External Clock": 0, - "channel": [ - { - "colour": "#16191a", - "enabled": true, - "index": 0, - "name": "0", - "strigger": 0, - "type": 10000 - }, - { - "colour": "#8f5202", - "enabled": true, - "index": 1, - "name": "1", - "strigger": 0, - "type": 10000 - }, - { - "colour": "#cc0000", - "enabled": true, - "index": 2, - "name": "2", - "strigger": 0, - "type": 10000 - }, - { - "colour": "#f57900", - "enabled": true, - "index": 3, - "name": "3", - "strigger": 0, - "type": 10000 - }, - { - "colour": "#edd400", - "enabled": true, - "index": 4, - "name": "4", - "strigger": 0, - "type": 10000 - }, - { - "colour": "#73d216", - "enabled": true, - "index": 5, - "name": "5", - "strigger": 0, - "type": 10000 - }, - { - "colour": "#3465a4", - "enabled": true, - "index": 6, - "name": "6", - "strigger": 0, - "type": 10000 - }, - { - "colour": "#75507b", - "enabled": true, - "index": 7, - "name": "7", - "strigger": 0, - "type": 10000 - }, - { - "colour": "#16191a", - "enabled": true, - "index": 8, - "name": "8", - "strigger": 0, - "type": 10000 - }, - { - "colour": "#8f5202", - "enabled": true, - "index": 9, - "name": "9", - "strigger": 0, - "type": 10000 - }, - { - "colour": "#cc0000", - "enabled": true, - "index": 10, - "name": "10", - "strigger": 0, - "type": 10000 - }, - { - "colour": "#f57900", - "enabled": true, - "index": 11, - "name": "11", - "strigger": 0, - "type": 10000 - }, - { - "colour": "#edd400", - "enabled": true, - "index": 12, - "name": "12", - "strigger": 0, - "type": 10000 - }, - { - "colour": "#73d216", - "enabled": true, - "index": 13, - "name": "13", - "strigger": 0, - "type": 10000 - }, - { - "colour": "#3465a4", - "enabled": true, - "index": 14, - "name": "14", - "strigger": 0, - "type": 10000 - }, - { - "colour": "#75507b", - "enabled": true, - "index": 15, - "name": "15", - "strigger": 0, - "type": 10000 - } - ], - "trigger": { - "triggerChannel": 0, - "triggerClock": "X X X X X X X X X X X X X X X X", - "triggerCount00": 1, - "triggerCount01": 1, - "triggerCount010": 1, - "triggerCount011": 1, - "triggerCount012": 1, - "triggerCount013": 1, - "triggerCount014": 1, - "triggerCount015": 1, - "triggerCount02": 1, - "triggerCount03": 1, - "triggerCount04": 1, - "triggerCount05": 1, - "triggerCount06": 1, - "triggerCount07": 1, - "triggerCount08": 1, - "triggerCount09": 1, - "triggerCount10": 1, - "triggerCount11": 1, - "triggerCount110": 1, - "triggerCount111": 1, - "triggerCount112": 1, - "triggerCount113": 1, - "triggerCount114": 1, - "triggerCount115": 1, - "triggerCount12": 1, - "triggerCount13": 1, - "triggerCount14": 1, - "triggerCount15": 1, - "triggerCount16": 1, - "triggerCount17": 1, - "triggerCount18": 1, - "triggerCount19": 1, - "triggerData": "X X X X X X X X X X X X X X X X", - "triggerInv00": 0, - "triggerInv01": 0, - "triggerInv010": 0, - "triggerInv011": 0, - "triggerInv012": 0, - "triggerInv013": 0, - "triggerInv014": 0, - "triggerInv015": 0, - "triggerInv02": 0, - "triggerInv03": 0, - "triggerInv04": 0, - "triggerInv05": 0, - "triggerInv06": 0, - "triggerInv07": 0, - "triggerInv08": 0, - "triggerInv09": 0, - "triggerInv10": 0, - "triggerInv11": 0, - "triggerInv110": 0, - "triggerInv111": 0, - "triggerInv112": 0, - "triggerInv113": 0, - "triggerInv114": 0, - "triggerInv115": 0, - "triggerInv12": 0, - "triggerInv13": 0, - "triggerInv14": 0, - "triggerInv15": 0, - "triggerInv16": 0, - "triggerInv17": 0, - "triggerInv18": 0, - "triggerInv19": 0, - "triggerLogic0": 1, - "triggerLogic1": 1, - "triggerLogic10": 1, - "triggerLogic11": 1, - "triggerLogic12": 1, - "triggerLogic13": 1, - "triggerLogic14": 1, - "triggerLogic15": 1, - "triggerLogic2": 1, - "triggerLogic3": 1, - "triggerLogic4": 1, - "triggerLogic5": 1, - "triggerLogic6": 1, - "triggerLogic7": 1, - "triggerLogic8": 1, - "triggerLogic9": 1, - "triggerMode": 0, - "triggerPos": 0, - "triggerSerial": 0, - "triggerStages": 0, - "triggerStart": "X X X X X X X X X X X X X X X X", - "triggerStop": "X X X X X X X X X X X X X X X X", - "triggerValue00": "X X X X X X X X X X X X X X X X", - "triggerValue01": "X X X X X X X X X X X X X X X X", - "triggerValue010": "X X X X X X X X X X X X X X X X", - "triggerValue011": "X X X X X X X X X X X X X X X X", - "triggerValue012": "X X X X X X X X X X X X X X X X", - "triggerValue013": "X X X X X X X X X X X X X X X X", - "triggerValue014": "X X X X X X X X X X X X X X X X", - "triggerValue015": "X X X X X X X X X X X X X X X X", - "triggerValue02": "X X X X X X X X X X X X X X X X", - "triggerValue03": "X X X X X X X X X X X X X X X X", - "triggerValue04": "X X X X X X X X X X X X X X X X", - "triggerValue05": "X X X X X X X X X X X X X X X X", - "triggerValue06": "X X X X X X X X X X X X X X X X", - "triggerValue07": "X X X X X X X X X X X X X X X X", - "triggerValue08": "X X X X X X X X X X X X X X X X", - "triggerValue09": "X X X X X X X X X X X X X X X X", - "triggerValue10": "X X X X X X X X X X X X X X X X", - "triggerValue11": "X X X X X X X X X X X X X X X X", - "triggerValue110": "X X X X X X X X X X X X X X X X", - "triggerValue111": "X X X X X X X X X X X X X X X X", - "triggerValue112": "X X X X X X X X X X X X X X X X", - "triggerValue113": "X X X X X X X X X X X X X X X X", - "triggerValue114": "X X X X X X X X X X X X X X X X", - "triggerValue115": "X X X X X X X X X X X X X X X X", - "triggerValue12": "X X X X X X X X X X X X X X X X", - "triggerValue13": "X X X X X X X X X X X X X X X X", - "triggerValue14": "X X X X X X X X X X X X X X X X", - "triggerValue15": "X X X X X X X X X X X X X X X X", - "triggerValue16": "X X X X X X X X X X X X X X X X", - "triggerValue17": "X X X X X X X X X X X X X X X X", - "triggerValue18": "X X X X X X X X X X X X X X X X", - "triggerValue19": "X X X X X X X X X X X X X X X X" - } -} +{ + "Channel Mode": "Use Channels 0~15 (Max 100MHz)", + "Device": "DSLogic", + "DeviceMode": 0, + "Filter Targets": "None", + "Horizontal trigger position": "0", + "Operation Mode": "Buffer Mode", + "Sample count": "1048576", + "Sample rate": "1000000", + "Threshold Level": 1, + "Trigger hold off": "0", + "Trigger slope": "0", + "Trigger source": "0", + "Using Clock Negedge": 0, + "Using External Clock": 0, + "channel": [ + { + "colour": "#6a6a6a", + "enabled": true, + "index": 0, + "name": "0", + "strigger": 0, + "type": 10000 + }, + { + "colour": "#6a6a6a", + "enabled": true, + "index": 1, + "name": "1", + "strigger": 0, + "type": 10000 + }, + { + "colour": "#6a6a6a", + "enabled": true, + "index": 2, + "name": "2", + "strigger": 0, + "type": 10000 + }, + { + "colour": "#6a6a6a", + "enabled": true, + "index": 3, + "name": "3", + "strigger": 0, + "type": 10000 + }, + { + "colour": "#6a6a6a", + "enabled": true, + "index": 4, + "name": "4", + "strigger": 0, + "type": 10000 + }, + { + "colour": "#6a6a6a", + "enabled": true, + "index": 5, + "name": "5", + "strigger": 0, + "type": 10000 + }, + { + "colour": "#6a6a6a", + "enabled": true, + "index": 6, + "name": "6", + "strigger": 0, + "type": 10000 + }, + { + "colour": "#6a6a6a", + "enabled": true, + "index": 7, + "name": "7", + "strigger": 0, + "type": 10000 + }, + { + "colour": "#6a6a6a", + "enabled": true, + "index": 8, + "name": "8", + "strigger": 0, + "type": 10000 + }, + { + "colour": "#6a6a6a", + "enabled": true, + "index": 9, + "name": "9", + "strigger": 0, + "type": 10000 + }, + { + "colour": "#6a6a6a", + "enabled": true, + "index": 10, + "name": "10", + "strigger": 0, + "type": 10000 + }, + { + "colour": "#6a6a6a", + "enabled": true, + "index": 11, + "name": "11", + "strigger": 0, + "type": 10000 + }, + { + "colour": "#6a6a6a", + "enabled": true, + "index": 12, + "name": "12", + "strigger": 0, + "type": 10000 + }, + { + "colour": "#6a6a6a", + "enabled": true, + "index": 13, + "name": "13", + "strigger": 0, + "type": 10000 + }, + { + "colour": "#6a6a6a", + "enabled": true, + "index": 14, + "name": "14", + "strigger": 0, + "type": 10000 + }, + { + "colour": "#6a6a6a", + "enabled": true, + "index": 15, + "name": "15", + "strigger": 0, + "type": 10000 + } + ], + "trigger": { + "triggerChannel": 0, + "triggerClock": "X X X X X X X X X X X X X X X X", + "triggerCount00": 1, + "triggerCount01": 1, + "triggerCount010": 1, + "triggerCount011": 1, + "triggerCount012": 1, + "triggerCount013": 1, + "triggerCount014": 1, + "triggerCount015": 1, + "triggerCount02": 1, + "triggerCount03": 1, + "triggerCount04": 1, + "triggerCount05": 1, + "triggerCount06": 1, + "triggerCount07": 1, + "triggerCount08": 1, + "triggerCount09": 1, + "triggerCount10": 1, + "triggerCount11": 1, + "triggerCount110": 1, + "triggerCount111": 1, + "triggerCount112": 1, + "triggerCount113": 1, + "triggerCount114": 1, + "triggerCount115": 1, + "triggerCount12": 1, + "triggerCount13": 1, + "triggerCount14": 1, + "triggerCount15": 1, + "triggerCount16": 1, + "triggerCount17": 1, + "triggerCount18": 1, + "triggerCount19": 1, + "triggerData": "X X X X X X X X X X X X X X X X", + "triggerInv00": 0, + "triggerInv01": 0, + "triggerInv010": 0, + "triggerInv011": 0, + "triggerInv012": 0, + "triggerInv013": 0, + "triggerInv014": 0, + "triggerInv015": 0, + "triggerInv02": 0, + "triggerInv03": 0, + "triggerInv04": 0, + "triggerInv05": 0, + "triggerInv06": 0, + "triggerInv07": 0, + "triggerInv08": 0, + "triggerInv09": 0, + "triggerInv10": 0, + "triggerInv11": 0, + "triggerInv110": 0, + "triggerInv111": 0, + "triggerInv112": 0, + "triggerInv113": 0, + "triggerInv114": 0, + "triggerInv115": 0, + "triggerInv12": 0, + "triggerInv13": 0, + "triggerInv14": 0, + "triggerInv15": 0, + "triggerInv16": 0, + "triggerInv17": 0, + "triggerInv18": 0, + "triggerInv19": 0, + "triggerLogic0": 1, + "triggerLogic1": 1, + "triggerLogic10": 1, + "triggerLogic11": 1, + "triggerLogic12": 1, + "triggerLogic13": 1, + "triggerLogic14": 1, + "triggerLogic15": 1, + "triggerLogic2": 1, + "triggerLogic3": 1, + "triggerLogic4": 1, + "triggerLogic5": 1, + "triggerLogic6": 1, + "triggerLogic7": 1, + "triggerLogic8": 1, + "triggerLogic9": 1, + "triggerMode": 0, + "triggerPos": 1, + "triggerSerial": 0, + "triggerStages": 0, + "triggerStart": "X X X X X X X X X X X X X X X X", + "triggerStop": "X X X X X X X X X X X X X X X X", + "triggerValue00": "X X X X X X X X X X X X X X X X", + "triggerValue01": "X X X X X X X X X X X X X X X X", + "triggerValue010": "X X X X X X X X X X X X X X X X", + "triggerValue011": "X X X X X X X X X X X X X X X X", + "triggerValue012": "X X X X X X X X X X X X X X X X", + "triggerValue013": "X X X X X X X X X X X X X X X X", + "triggerValue014": "X X X X X X X X X X X X X X X X", + "triggerValue015": "X X X X X X X X X X X X X X X X", + "triggerValue02": "X X X X X X X X X X X X X X X X", + "triggerValue03": "X X X X X X X X X X X X X X X X", + "triggerValue04": "X X X X X X X X X X X X X X X X", + "triggerValue05": "X X X X X X X X X X X X X X X X", + "triggerValue06": "X X X X X X X X X X X X X X X X", + "triggerValue07": "X X X X X X X X X X X X X X X X", + "triggerValue08": "X X X X X X X X X X X X X X X X", + "triggerValue09": "X X X X X X X X X X X X X X X X", + "triggerValue10": "X X X X X X X X X X X X X X X X", + "triggerValue11": "X X X X X X X X X X X X X X X X", + "triggerValue110": "X X X X X X X X X X X X X X X X", + "triggerValue111": "X X X X X X X X X X X X X X X X", + "triggerValue112": "X X X X X X X X X X X X X X X X", + "triggerValue113": "X X X X X X X X X X X X X X X X", + "triggerValue114": "X X X X X X X X X X X X X X X X", + "triggerValue115": "X X X X X X X X X X X X X X X X", + "triggerValue12": "X X X X X X X X X X X X X X X X", + "triggerValue13": "X X X X X X X X X X X X X X X X", + "triggerValue14": "X X X X X X X X X X X X X X X X", + "triggerValue15": "X X X X X X X X X X X X X X X X", + "triggerValue16": "X X X X X X X X X X X X X X X X", + "triggerValue17": "X X X X X X X X X X X X X X X X", + "triggerValue18": "X X X X X X X X X X X X X X X X", + "triggerValue19": "X X X X X X X X X X X X X X X X", + "triggerVcnt": 1 + } +} diff --git a/DSView/res/DSLogic0.dsc b/DSView/res/DSLogic0.dsc deleted file mode 100644 index 780b982f..00000000 --- a/DSView/res/DSLogic0.dsc +++ /dev/null @@ -1,268 +0,0 @@ -{ - "Device": "DSLogic", - "DeviceMode": 0, - "Filter Targets": "None", - "Horizontal trigger position": "0", - "Operation Mode": "Normal", - "Sample count": "16777216", - "Sample rate": "100000000", - "Threshold Level": 1, - "Trigger hold off": "0", - "Trigger slope": "0", - "Trigger source": "0", - "Using Clock Negedge": 0, - "Using External Clock": 0, - "channel": [ - { - "colour": "#16191a", - "enabled": true, - "index": 0, - "name": "0", - "strigger": 0, - "type": 10000 - }, - { - "colour": "#8f5202", - "enabled": true, - "index": 1, - "name": "1", - "strigger": 0, - "type": 10000 - }, - { - "colour": "#cc0000", - "enabled": true, - "index": 2, - "name": "2", - "strigger": 0, - "type": 10000 - }, - { - "colour": "#f57900", - "enabled": true, - "index": 3, - "name": "3", - "strigger": 0, - "type": 10000 - }, - { - "colour": "#edd400", - "enabled": true, - "index": 4, - "name": "4", - "strigger": 0, - "type": 10000 - }, - { - "colour": "#73d216", - "enabled": true, - "index": 5, - "name": "5", - "strigger": 0, - "type": 10000 - }, - { - "colour": "#3465a4", - "enabled": true, - "index": 6, - "name": "6", - "strigger": 0, - "type": 10000 - }, - { - "colour": "#75507b", - "enabled": true, - "index": 7, - "name": "7", - "strigger": 0, - "type": 10000 - }, - { - "colour": "#16191a", - "enabled": true, - "index": 8, - "name": "8", - "strigger": 0, - "type": 10000 - }, - { - "colour": "#8f5202", - "enabled": true, - "index": 9, - "name": "9", - "strigger": 0, - "type": 10000 - }, - { - "colour": "#cc0000", - "enabled": true, - "index": 10, - "name": "10", - "strigger": 0, - "type": 10000 - }, - { - "colour": "#f57900", - "enabled": true, - "index": 11, - "name": "11", - "strigger": 0, - "type": 10000 - }, - { - "colour": "#edd400", - "enabled": true, - "index": 12, - "name": "12", - "strigger": 0, - "type": 10000 - }, - { - "colour": "#73d216", - "enabled": true, - "index": 13, - "name": "13", - "strigger": 0, - "type": 10000 - }, - { - "colour": "#3465a4", - "enabled": true, - "index": 14, - "name": "14", - "strigger": 0, - "type": 10000 - }, - { - "colour": "#75507b", - "enabled": true, - "index": 15, - "name": "15", - "strigger": 0, - "type": 10000 - } - ], - "trigger": { - "triggerChannel": 0, - "triggerClock": "X X X X X X X X X X X X X X X X", - "triggerCount00": 1, - "triggerCount01": 1, - "triggerCount010": 1, - "triggerCount011": 1, - "triggerCount012": 1, - "triggerCount013": 1, - "triggerCount014": 1, - "triggerCount015": 1, - "triggerCount02": 1, - "triggerCount03": 1, - "triggerCount04": 1, - "triggerCount05": 1, - "triggerCount06": 1, - "triggerCount07": 1, - "triggerCount08": 1, - "triggerCount09": 1, - "triggerCount10": 1, - "triggerCount11": 1, - "triggerCount110": 1, - "triggerCount111": 1, - "triggerCount112": 1, - "triggerCount113": 1, - "triggerCount114": 1, - "triggerCount115": 1, - "triggerCount12": 1, - "triggerCount13": 1, - "triggerCount14": 1, - "triggerCount15": 1, - "triggerCount16": 1, - "triggerCount17": 1, - "triggerCount18": 1, - "triggerCount19": 1, - "triggerData": "X X X X X X X X X X X X X X X X", - "triggerInv00": 0, - "triggerInv01": 0, - "triggerInv010": 0, - "triggerInv011": 0, - "triggerInv012": 0, - "triggerInv013": 0, - "triggerInv014": 0, - "triggerInv015": 0, - "triggerInv02": 0, - "triggerInv03": 0, - "triggerInv04": 0, - "triggerInv05": 0, - "triggerInv06": 0, - "triggerInv07": 0, - "triggerInv08": 0, - "triggerInv09": 0, - "triggerInv10": 0, - "triggerInv11": 0, - "triggerInv110": 0, - "triggerInv111": 0, - "triggerInv112": 0, - "triggerInv113": 0, - "triggerInv114": 0, - "triggerInv115": 0, - "triggerInv12": 0, - "triggerInv13": 0, - "triggerInv14": 0, - "triggerInv15": 0, - "triggerInv16": 0, - "triggerInv17": 0, - "triggerInv18": 0, - "triggerInv19": 0, - "triggerLogic0": 1, - "triggerLogic1": 1, - "triggerLogic10": 1, - "triggerLogic11": 1, - "triggerLogic12": 1, - "triggerLogic13": 1, - "triggerLogic14": 1, - "triggerLogic15": 1, - "triggerLogic2": 1, - "triggerLogic3": 1, - "triggerLogic4": 1, - "triggerLogic5": 1, - "triggerLogic6": 1, - "triggerLogic7": 1, - "triggerLogic8": 1, - "triggerLogic9": 1, - "triggerMode": 0, - "triggerPos": 0, - "triggerSerial": 0, - "triggerStages": 0, - "triggerStart": "X X X X X X X X X X X X X X X X", - "triggerStop": "X X X X X X X X X X X X X X X X", - "triggerValue00": "X X X X X X X X X X X X X X X X", - "triggerValue01": "X X X X X X X X X X X X X X X X", - "triggerValue010": "X X X X X X X X X X X X X X X X", - "triggerValue011": "X X X X X X X X X X X X X X X X", - "triggerValue012": "X X X X X X X X X X X X X X X X", - "triggerValue013": "X X X X X X X X X X X X X X X X", - "triggerValue014": "X X X X X X X X X X X X X X X X", - "triggerValue015": "X X X X X X X X X X X X X X X X", - "triggerValue02": "X X X X X X X X X X X X X X X X", - "triggerValue03": "X X X X X X X X X X X X X X X X", - "triggerValue04": "X X X X X X X X X X X X X X X X", - "triggerValue05": "X X X X X X X X X X X X X X X X", - "triggerValue06": "X X X X X X X X X X X X X X X X", - "triggerValue07": "X X X X X X X X X X X X X X X X", - "triggerValue08": "X X X X X X X X X X X X X X X X", - "triggerValue09": "X X X X X X X X X X X X X X X X", - "triggerValue10": "X X X X X X X X X X X X X X X X", - "triggerValue11": "X X X X X X X X X X X X X X X X", - "triggerValue110": "X X X X X X X X X X X X X X X X", - "triggerValue111": "X X X X X X X X X X X X X X X X", - "triggerValue112": "X X X X X X X X X X X X X X X X", - "triggerValue113": "X X X X X X X X X X X X X X X X", - "triggerValue114": "X X X X X X X X X X X X X X X X", - "triggerValue115": "X X X X X X X X X X X X X X X X", - "triggerValue12": "X X X X X X X X X X X X X X X X", - "triggerValue13": "X X X X X X X X X X X X X X X X", - "triggerValue14": "X X X X X X X X X X X X X X X X", - "triggerValue15": "X X X X X X X X X X X X X X X X", - "triggerValue16": "X X X X X X X X X X X X X X X X", - "triggerValue17": "X X X X X X X X X X X X X X X X", - "triggerValue18": "X X X X X X X X X X X X X X X X", - "triggerValue19": "X X X X X X X X X X X X X X X X" - } -} diff --git a/DSView/res/DSLogic1.def.dsc b/DSView/res/DSLogic1.def.dsc old mode 100644 new mode 100755 index 06a4a02f..75ab6ead --- a/DSView/res/DSLogic1.def.dsc +++ b/DSView/res/DSLogic1.def.dsc @@ -1,13 +1,15 @@ -{ +{ + "Channel Mode": "Use Channels 0~15 (Max 100MHz)", "Device": "DSLogic", "DeviceMode": 1, "Filter Targets": "None", - "Horizontal trigger position": "0", - "Operation Mode": "Normal", + "Horizontal trigger position": "1", + "Operation Mode": "Buffer Mode", "Sample count": "1048576", "Sample rate": "100000000", "Threshold Level": "1.8/2.5/3.3V Level", "Trigger hold off": "0", + "Trigger margin": "8", "Trigger slope": "0", "Trigger source": "0", "Using Clock Negedge": 0, @@ -19,7 +21,6 @@ "enabled": true, "index": 0, "name": "0", - "strigger": 0, "trigValue": 0.5, "type": 10001, "vdiv": 1000, @@ -32,7 +33,6 @@ "enabled": true, "index": 1, "name": "1", - "strigger": 0, "trigValue": 0.5, "type": 10001, "vdiv": 1000, diff --git a/DSView/res/DSLogic1.dsc b/DSView/res/DSLogic1.dsc deleted file mode 100644 index 06a4a02f..00000000 --- a/DSView/res/DSLogic1.dsc +++ /dev/null @@ -1,43 +0,0 @@ -{ - "Device": "DSLogic", - "DeviceMode": 1, - "Filter Targets": "None", - "Horizontal trigger position": "0", - "Operation Mode": "Normal", - "Sample count": "1048576", - "Sample rate": "100000000", - "Threshold Level": "1.8/2.5/3.3V Level", - "Trigger hold off": "0", - "Trigger slope": "0", - "Trigger source": "0", - "Using Clock Negedge": 0, - "Using External Clock": 0, - "channel": [ - { - "colour": "#eeb211", - "coupling": 0, - "enabled": true, - "index": 0, - "name": "0", - "strigger": 0, - "trigValue": 0.5, - "type": 10001, - "vdiv": 1000, - "vfactor": 1, - "zeroPos": 0.5 - }, - { - "colour": "#009925", - "coupling": 0, - "enabled": true, - "index": 1, - "name": "1", - "strigger": 0, - "trigValue": 0.5, - "type": 10001, - "vdiv": 1000, - "vfactor": 1, - "zeroPos": 0.5 - } - ] -} diff --git a/DSView/res/DSLogic2.def.dsc b/DSView/res/DSLogic2.def.dsc old mode 100644 new mode 100755 index 7858fcad..37160a12 --- a/DSView/res/DSLogic2.def.dsc +++ b/DSView/res/DSLogic2.def.dsc @@ -1,10 +1,11 @@ -{ +{ + "Channel Mode": "Use Channels 0~15 (Max 100MHz)", "Device": "DSLogic", "DeviceMode": 2, "Filter Targets": "None", - "Horizontal trigger position": "0", - "Operation Mode": "Normal", - "Sample count": "16777216", + "Horizontal trigger position": "245", + "Operation Mode": "Buffer Mode", + "Sample count": "1048576", "Sample rate": "100000000", "Threshold Level": "1.8/2.5/3.3V Level", "Trigger hold off": "0", @@ -18,7 +19,6 @@ "enabled": true, "index": 0, "name": "0", - "strigger": 0, "type": 10002 }, { @@ -26,7 +26,6 @@ "enabled": true, "index": 1, "name": "1", - "strigger": 0, "type": 10002 }, { @@ -34,7 +33,6 @@ "enabled": true, "index": 2, "name": "2", - "strigger": 0, "type": 10002 }, { @@ -42,7 +40,6 @@ "enabled": true, "index": 3, "name": "3", - "strigger": 0, "type": 10002 }, { @@ -50,7 +47,6 @@ "enabled": true, "index": 4, "name": "4", - "strigger": 0, "type": 10002 }, { @@ -58,7 +54,6 @@ "enabled": true, "index": 5, "name": "5", - "strigger": 0, "type": 10002 }, { @@ -66,7 +61,6 @@ "enabled": true, "index": 6, "name": "6", - "strigger": 0, "type": 10002 }, { @@ -74,7 +68,6 @@ "enabled": true, "index": 7, "name": "7", - "strigger": 0, "type": 10002 }, { @@ -82,7 +75,6 @@ "enabled": true, "index": 8, "name": "8", - "strigger": 0, "type": 10002 } ] diff --git a/DSView/res/DSLogic2.dsc b/DSView/res/DSLogic2.dsc deleted file mode 100644 index 7858fcad..00000000 --- a/DSView/res/DSLogic2.dsc +++ /dev/null @@ -1,89 +0,0 @@ -{ - "Device": "DSLogic", - "DeviceMode": 2, - "Filter Targets": "None", - "Horizontal trigger position": "0", - "Operation Mode": "Normal", - "Sample count": "16777216", - "Sample rate": "100000000", - "Threshold Level": "1.8/2.5/3.3V Level", - "Trigger hold off": "0", - "Trigger slope": "0", - "Trigger source": "0", - "Using Clock Negedge": 0, - "Using External Clock": 0, - "channel": [ - { - "colour": "#1185d1", - "enabled": true, - "index": 0, - "name": "0", - "strigger": 0, - "type": 10002 - }, - { - "colour": "#eeb211", - "enabled": true, - "index": 1, - "name": "1", - "strigger": 0, - "type": 10002 - }, - { - "colour": "#d50f25", - "enabled": true, - "index": 2, - "name": "2", - "strigger": 0, - "type": 10002 - }, - { - "colour": "#009925", - "enabled": true, - "index": 3, - "name": "3", - "strigger": 0, - "type": 10002 - }, - { - "colour": "#1185d1", - "enabled": true, - "index": 4, - "name": "4", - "strigger": 0, - "type": 10002 - }, - { - "colour": "#eeb211", - "enabled": true, - "index": 5, - "name": "5", - "strigger": 0, - "type": 10002 - }, - { - "colour": "#d50f25", - "enabled": true, - "index": 6, - "name": "6", - "strigger": 0, - "type": 10002 - }, - { - "colour": "#009925", - "enabled": true, - "index": 7, - "name": "7", - "strigger": 0, - "type": 10002 - }, - { - "colour": "#1185d1", - "enabled": true, - "index": 8, - "name": "8", - "strigger": 0, - "type": 10002 - } - ] -} diff --git a/DSView/res/DSLogic33.bin b/DSView/res/DSLogic33.bin old mode 100644 new mode 100755 index 849cd1df837a65270ed4b5788cd6ca4c42a83b9c..cac826191e2def2e7c8bc564c071a4fed0d8796d GIT binary patch literal 340884 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

4tHc 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
Y`SXvw{gLV2%O47>FLv)t4lEjmahmY>-ksI8 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 IA?~ODKcra>Q2+n{ diff --git a/DSView/res/DSLogic50.bin b/DSView/res/DSLogic50.bin old mode 100644 new mode 100755 index afb8b0aefc3ec68eced690ba33a252faa96c1f4b..50bef2329a6909d88b8f4cab32eea191be836fbe GIT binary patch literal 340884 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 MaD8^*crEk)0>k*}a{vGU diff --git a/DSView/res/DSLogicPro.bin b/DSView/res/DSLogicPro.bin old mode 100644 new mode 100755 index c2bfdfae0d93caa55cb5eee0701279a42c8ae0b1..9074fd5706aae5aa8fd11181818e1a28bd58112d GIT binary patch literal 340884 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 diff --git a/DSView/res/DSLogicPro.fw b/DSView/res/DSLogicPro.fw old mode 100644 new mode 100755 index 65beceb7842eb6cddce548f8acae24ec0f714336..22924c2b53dfb6deb0b087c9f019ba3f155ad999 GIT binary patch delta 2845 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 0.22 This is part of the standard OpenBSD install (not an extra package), apparently. - check >= 0.9.4 (optional, only needed to run unit tests) + - libfftw3 >= 3.3 Building and installing ----------------------- @@ -35,17 +36,17 @@ please check your respective distro's package manager tool if you use other dist Debian/Ubuntu: $ sudo apt-get install git-core gcc g++ make cmake autoconf automake libtool pkg-config \ libglib2.0-dev libzip-dev libudev-dev libusb-1.0-0-dev \ - python3-dev qt5-default libboost-dev libboost-test-dev libboost-thread-dev libboost-system-dev libboost-filesystem-dev check + python3-dev qt5-default libboost-dev libboost-test-dev libboost-thread-dev libboost-system-dev libboost-filesystem-dev check libfftw3-dev Fedora (18, 19): $ sudo yum install git gcc g++ make cmake autoconf automake libtool pkgconfig glib2-devel \ libzip-devel libudev-devel libusb1-devel \ - python3-devel qt-devel boost-devel check + python3-devel qt-devel boost-devel check libfftw3-devel Arch: $ pacman -S git gcc make cmake autoconf autoconf-archive automake libtool \ pkg-config glib2 glibmm libzip libusb check - python boost qt5 qt5-base qt5-svg + python boost qt5 qt5-base qt5-svg libfftw3 Step2: Get the DSView source code @@ -60,8 +61,7 @@ Step3: Building $ sudo make install $ cd .. - $ git clone git://sigrok.org/libsigrokdecode - $ cd libsigrokdecode + $ cd libsigrokdecode4DSL $ ./autogen.sh $ ./configure $ make diff --git a/libsigrok4DSL/hardware/DSL/command.c b/libsigrok4DSL/hardware/DSL/command.c index e65741ed..b23e852e 100644 --- a/libsigrok4DSL/hardware/DSL/command.c +++ b/libsigrok4DSL/hardware/DSL/command.c @@ -305,3 +305,22 @@ SR_PRIV int command_rd_nvm(libusb_device_handle *devhdl, unsigned char *ctx, uin return SR_OK; } + +SR_PRIV int command_get_fpga_done(libusb_device_handle *devhdl, + uint8_t *fpga_done) +{ + int ret; + + ret = libusb_control_transfer(devhdl, LIBUSB_REQUEST_TYPE_VENDOR | + LIBUSB_ENDPOINT_IN, CMD_FPGA_DONE, 0x0000, 0x0000, + fpga_done, 1, 3000); + + if (ret < 0) { + sr_err("Unable to get fpga done info: %s.", + libusb_error_name(ret)); + return SR_ERR; + } + + return SR_OK; +} + diff --git a/libsigrok4DSL/hardware/DSL/command.h b/libsigrok4DSL/hardware/DSL/command.h index 44485e24..352fafb2 100644 --- a/libsigrok4DSL/hardware/DSL/command.h +++ b/libsigrok4DSL/hardware/DSL/command.h @@ -37,6 +37,7 @@ #define CMD_WR_NVM 0xb9 #define CMD_RD_NVM 0xba #define CMD_RD_NVM_PRE 0xbb +#define CMD_FPGA_DONE 0xbc #define CMD_START_FLAGS_MODE_POS 4 #define CMD_START_FLAGS_WIDE_POS 5 @@ -165,4 +166,6 @@ SR_PRIV int command_wr_reg(libusb_device_handle *devhdl, uint8_t value, uint8_t SR_PRIV int command_wr_nvm(libusb_device_handle *devhdl, unsigned char *ctx, uint8_t len); SR_PRIV int command_rd_nvm(libusb_device_handle *devhdl, unsigned char *ctx, uint8_t addr, uint8_t len); +SR_PRIV int command_get_fpga_done(libusb_device_handle *devhdl, + uint8_t *fpga_done); #endif diff --git a/libsigrok4DSL/hardware/DSL/dscope.c b/libsigrok4DSL/hardware/DSL/dscope.c index b3d1f71c..b355ebb5 100644 --- a/libsigrok4DSL/hardware/DSL/dscope.c +++ b/libsigrok4DSL/hardware/DSL/dscope.c @@ -89,6 +89,7 @@ static const int32_t sessions[] = { SR_CONF_TIMEBASE, SR_CONF_TRIGGER_SLOPE, SR_CONF_TRIGGER_SOURCE, + SR_CONF_TRIGGER_CHANNEL, SR_CONF_HORIZ_TRIGGERPOS, SR_CONF_TRIGGER_HOLDOFF, SR_CONF_TRIGGER_MARGIN, @@ -239,6 +240,20 @@ static gboolean check_conf_profile(libusb_device *dev) return ret; } +static int en_ch_num(const struct sr_dev_inst *sdi) +{ + GSList *l; + int channel_en_cnt = 0; + + for (l = sdi->channels; l; l = l->next) { + struct sr_channel *probe = (struct sr_channel *)l->data; + channel_en_cnt += probe->enabled; + } + channel_en_cnt += (channel_en_cnt == 0); + + return channel_en_cnt; +} + static int fpga_setting(const struct sr_dev_inst *sdi) { struct DSL_context *devc; @@ -249,9 +264,6 @@ static int fpga_setting(const struct sr_dev_inst *sdi) int transferred; int result; int i; - int channel_en_cnt = 0; - int channel_cnt = 0; - GSList *l; devc = sdi->priv; usb = sdi->conn; @@ -287,26 +299,19 @@ static int fpga_setting(const struct sr_dev_inst *sdi) //setting.trig_logic3_header = 0x2310ffff; setting.end_sync = 0xfa5afa5a; - for (l = sdi->channels; l; l = l->next) { - struct sr_channel *probe = (struct sr_channel *)l->data; - channel_en_cnt += probe->enabled; - channel_cnt++; - } - if (channel_en_cnt == 0) - channel_en_cnt = 1; //setting.mode = (test_mode ? 0x8000 : 0x0000) + trigger->trigger_en + (sdi->mode << 4); setting.mode = ((devc->op_mode == SR_OP_INTERNAL_TEST) << 15) + ((devc->op_mode == SR_OP_EXTERNAL_TEST) << 14) + ((devc->op_mode == SR_OP_LOOPBACK_TEST) << 13) + trigger->trigger_en + - ((sdi->mode > 0) << 4) + (devc->clock_type << 1) + (devc->clock_edge << 2) + + ((sdi->mode == DSO) << 4) + (devc->clock_type << 1) + (devc->clock_edge << 2) + (((devc->cur_samplerate == SR_MHZ(200) && sdi->mode != DSO) || (sdi->mode == ANALOG)) << 5) + ((devc->cur_samplerate == SR_MHZ(400)) << 6) + ((sdi->mode == ANALOG) << 7) + ((devc->filter == SR_FILTER_1T) << 8) + (devc->instant << 9); - setting.divider = (uint32_t)ceil(DSCOPE_MAX_SAMPLERATE * 1.0 / devc->cur_samplerate / channel_en_cnt); - setting.count = (uint32_t)(devc->limit_samples / (channel_cnt / channel_en_cnt)); + setting.divider = (uint32_t)ceil(DSCOPE_MAX_SAMPLERATE * 1.0 / devc->cur_samplerate / en_ch_num(sdi)); + setting.count = (uint32_t)(devc->limit_samples / (g_slist_length(sdi->channels) / en_ch_num(sdi))); setting.trig_pos = (uint32_t)(trigger->trigger_pos / 100.0 * devc->limit_samples); setting.trig_glb = trigger->trigger_stages; setting.trig_adp = setting.count - setting.trig_pos - 1; @@ -638,7 +643,8 @@ static struct DSL_context *DSCope_dev_new(void) devc->data_lock = FALSE; devc->cali = FALSE; devc->dso_bits = 8; - devc->trigger_margin = 0; + devc->trigger_margin = 8; + devc->trigger_channel = 0; return devc; } @@ -707,6 +713,7 @@ static uint64_t get_default_vgain(struct sr_dev_inst *sdi, int num) static int probe_init(struct sr_dev_inst *sdi) { + int i; GList *l; for (l = sdi->channels; l; l = l->next) { struct sr_channel *probe = (struct sr_channel *)l->data; @@ -717,6 +724,9 @@ static int probe_init(struct sr_dev_inst *sdi) probe->coupling = SR_DC_COUPLING; probe->trig_value = 0x80; probe->vpos_trans = get_default_trans(sdi); + probe->ms_show = TRUE; + for (i = DSO_MS_BEGIN; i < DSO_MS_END; i++) + probe->ms_en[i] = default_ms_en[i]; } } } @@ -840,10 +850,10 @@ static GSList *scan(GSList *options) return NULL; devc = DSCope_dev_new(); - devc->profile = prof; - sdi->priv = devc; - drvc->instances = g_slist_append(drvc->instances, sdi); - devices = g_slist_append(devices, sdi); + devc->profile = prof; + sdi->priv = devc; + drvc->instances = g_slist_append(drvc->instances, sdi); + //devices = g_slist_append(devices, sdi); if (check_conf_profile(devlist[i])) { /* Already has the firmware, so fix the new address. */ @@ -852,6 +862,8 @@ static GSList *scan(GSList *options) sdi->inst_type = SR_INST_USB; sdi->conn = sr_usb_dev_inst_new(libusb_get_bus_number(devlist[i]), libusb_get_device_address(devlist[i]), NULL); + /* only report device after firmware is ready */ + devices = g_slist_append(devices, sdi); } else { char *firmware = malloc(strlen(DS_RES_PATH)+strlen(prof->firmware)+1); if (firmware == NULL) @@ -953,24 +965,18 @@ static uint64_t dso_cmd_gen(struct sr_dev_inst *sdi, struct sr_channel* ch, int { struct DSL_context *devc; uint64_t cmd = 0; - int channel_cnt = 0; uint64_t vpos; GSList *l; const int ch_bit = 7; - devc = sdi->priv; switch (id) { case SR_CONF_EN_CH: case SR_CONF_COUPLING: - for (l = sdi->channels; l; l = l->next) { - struct sr_channel *probe = (struct sr_channel *)l->data; - channel_cnt += probe->enabled; - } - if (devc->zero || channel_cnt == 2) { + if (devc->zero || en_ch_num(sdi) == 2) { cmd += 0x0E00; //cmd += 0x000; - } else if (channel_cnt == 1) { + } else if (en_ch_num(sdi) == 1) { if (((ch->index == 0) && ch->enabled) || ((ch->index == 1) && !ch->enabled)) cmd += 0x1600; else if (((ch->index == 1) && ch->enabled) || ((ch->index == 0) && !ch->enabled)) @@ -999,12 +1005,8 @@ static uint64_t dso_cmd_gen(struct sr_dev_inst *sdi, struct sr_channel* ch, int cmd += (vpos << 8); break; case SR_CONF_SAMPLERATE: - for (l = sdi->channels; l; l = l->next) { - struct sr_channel *probe = (struct sr_channel *)l->data; - channel_cnt += probe->enabled; - } cmd += 0x18; - uint32_t divider = devc->zero ? 0x1 : (uint32_t)ceil(DSCOPE_MAX_SAMPLERATE * 1.0 / devc->cur_samplerate / channel_cnt); + uint32_t divider = devc->zero ? 0x1 : (uint32_t)ceil(DSCOPE_MAX_SAMPLERATE * 1.0 / devc->cur_samplerate / en_ch_num(sdi)); cmd += divider << 8; break; case SR_CONF_HORIZ_TRIGGERPOS: @@ -1044,7 +1046,7 @@ static uint64_t dso_cmd_gen(struct sr_dev_inst *sdi, struct sr_channel* ch, int return cmd; } -static gboolean dso_load_eep(struct sr_dev_inst *sdi, struct sr_channel *probe) +static gboolean dso_load_eep(struct sr_dev_inst *sdi, struct sr_channel *probe, gboolean fpga_done) { int ret, i; struct sr_usb_dev_inst *usb = sdi->conn; @@ -1054,7 +1056,7 @@ static gboolean dso_load_eep(struct sr_dev_inst *sdi, struct sr_channel *probe) zero_info.zero_addr = dst_addr; if ((ret = command_rd_nvm(usb->devhdl, (unsigned char *)&zero_info, zero_info.zero_addr, sizeof(struct cmd_zero_info))) != SR_OK) { return FALSE; - sr_err("Send Get Zero command failed!"); + sr_err("%s: Send Get Zero command failed!", __func__); } else { if (zero_info.zero_addr == dst_addr) { uint8_t* voff_ptr = &zero_info.zero_addr + 1; @@ -1069,13 +1071,15 @@ static gboolean dso_load_eep(struct sr_dev_inst *sdi, struct sr_channel *probe) probe->comb_diff_top = *(voff_ptr + 2*i); probe->comb_diff_bom = *(voff_ptr + 2*i + 1); probe->vpos_trans = *(voff_ptr + 2*i + 2) + (*(voff_ptr + 2*i + 3) << 8); - const double slope = (probe->comb_diff_bom - probe->comb_diff_top)/(2.0*255.0); - for (i = 0; i < 256; i++) { - ret = command_wr_reg(usb->devhdl, i, COMB_ADDR + probe->index*2); - int value = i+i*slope+probe->comb_diff_top*0.5+0.5; - value = (value < 0) ? 0 : - (value > 255) ? 255 : value; - ret = command_wr_reg(usb->devhdl, value, COMB_ADDR + probe->index*2 + 1); + if (!fpga_done) { + const double slope = (probe->comb_diff_bom - probe->comb_diff_top)/(2.0*255.0); + for (i = 0; i < 256; i++) { + ret = command_wr_reg(usb->devhdl, i, COMB_ADDR + probe->index*2); + int value = i+i*slope+probe->comb_diff_top*0.5+0.5; + value = (value < 0) ? 0 : + (value > 255) ? 255 : value; + ret = command_wr_reg(usb->devhdl, value, COMB_ADDR + probe->index*2 + 1); + } } } } else { @@ -1087,7 +1091,7 @@ static gboolean dso_load_eep(struct sr_dev_inst *sdi, struct sr_channel *probe) vga_info.vga_addr = dst_addr + sizeof(struct cmd_zero_info); if ((ret = command_rd_nvm(usb->devhdl, (unsigned char *)&vga_info, vga_info.vga_addr, sizeof(struct cmd_vga_info))) != SR_OK) { return FALSE; - sr_err("Send Get Zero command failed!"); + sr_err("%s: Send Get Zero command failed!", __func__); } else { if (vga_info.vga_addr == dst_addr + sizeof(struct cmd_zero_info)) { uint16_t* vgain_ptr = &vga_info.vga0; @@ -1225,7 +1229,13 @@ static int config_get(int id, GVariant **data, const struct sr_dev_inst *sdi, if (!sdi) return SR_ERR; devc = sdi->priv; - *data = g_variant_new_byte(devc->trigger_source); + *data = g_variant_new_byte(devc->trigger_source&0x0f); + break; + case SR_CONF_TRIGGER_CHANNEL: + if (!sdi) + return SR_ERR; + devc = sdi->priv; + *data = g_variant_new_byte(devc->trigger_source>>4); break; case SR_CONF_TRIGGER_VALUE: if (!ch) @@ -1236,7 +1246,11 @@ static int config_get(int id, GVariant **data, const struct sr_dev_inst *sdi, if (!sdi) return SR_ERR; devc = sdi->priv; - *data = g_variant_new_byte(devc->trigger_hrate); + if (sdi->mode == DSO) { + *data = g_variant_new_byte(devc->trigger_hrate); + } else { + *data = g_variant_new_byte(devc->trigger_hpos); + } break; case SR_CONF_TRIGGER_HOLDOFF: if (!sdi) @@ -1265,7 +1279,8 @@ static int config_get(int id, GVariant **data, const struct sr_dev_inst *sdi, case SR_CONF_STREAM: if (!sdi) return SR_ERR; - *data = g_variant_new_boolean(FALSE); + devc = sdi->priv; + *data = g_variant_new_boolean(devc->stream); break; case SR_CONF_TEST: if (!sdi) @@ -1369,15 +1384,18 @@ static int config_set(int id, GVariant *data, struct sr_dev_inst *sdi, const char *stropt; int ret, num_probes; struct sr_usb_dev_inst *usb; + struct drv_context *drvc; (void)cg; if (sdi->status != SR_ST_ACTIVE) return SR_ERR; + drvc = di->priv; devc = sdi->priv; usb = sdi->conn; + ret = SR_OK; if (id == SR_CONF_SAMPLERATE) { devc->cur_samplerate = g_variant_get_uint64(data); if (sdi->mode == LOGIC) { @@ -1386,38 +1404,25 @@ static int config_set(int id, GVariant *data, struct sr_dev_inst *sdi, } else { adjust_probes(sdi, 16); } - ret = SR_OK; } else if(sdi->mode == DSO) { ret = command_dso_ctrl(usb->devhdl, dso_cmd_gen(sdi, 0, SR_CONF_SAMPLERATE)); } - } else if (id == SR_CONF_CLOCK_TYPE) { devc->clock_type = g_variant_get_boolean(data); - ret = SR_OK; } else if (id == SR_CONF_CLOCK_EDGE) { devc->clock_edge = g_variant_get_boolean(data); - ret = SR_OK; } else if (id == SR_CONF_INSTANT) { devc->instant = g_variant_get_boolean(data); - int num_probes = 0; - GSList *l; - for (l = sdi->channels; l; l = l->next) { - struct sr_channel *probe = (struct sr_channel *)l->data; - num_probes += probe->enabled; - } - if (num_probes != 0) { + if (en_ch_num(sdi) != 0) { if (devc->instant) - devc->limit_samples = DSCOPE_INSTANT_DEPTH / num_probes; + devc->limit_samples = DSCOPE_INSTANT_DEPTH / en_ch_num(sdi); else - devc->limit_samples = DSCOPE_MAX_DEPTH / num_probes; + devc->limit_samples = DSCOPE_MAX_DEPTH / en_ch_num(sdi); } - ret = SR_OK; } else if (id == SR_CONF_LIMIT_SAMPLES) { devc->limit_samples = g_variant_get_uint64(data); - ret = SR_OK; } else if (id == SR_CONF_DEVICE_MODE) { sdi->mode = g_variant_get_int16(data); - ret = SR_OK; if (sdi->mode == LOGIC) { num_probes = devc->profile->dev_caps & DEV_CAPS_16BIT ? 16 : 8; } else if (sdi->mode == DSO) { @@ -1441,7 +1446,6 @@ static int config_set(int id, GVariant *data, struct sr_dev_inst *sdi, sr_dbg("%s: setting mode to %d", __func__, sdi->mode); } else if (id == SR_CONF_OPERATION_MODE) { stropt = g_variant_get_string(data, NULL); - ret = SR_OK; if (!strcmp(stropt, opmodes[SR_OP_BUFFER])) { devc->op_mode = SR_OP_BUFFER; } else if (!strcmp(stropt, opmodes[SR_OP_INTERNAL_TEST])) { @@ -1457,7 +1461,6 @@ static int config_set(int id, GVariant *data, struct sr_dev_inst *sdi, __func__, devc->op_mode); } else if (id == SR_CONF_THRESHOLD) { stropt = g_variant_get_string(data, NULL); - ret = SR_OK; if (!strcmp(stropt, thresholds[SR_TH_3V3])) { devc->th_level = SR_TH_3V3; } else if (!strcmp(stropt, thresholds[SR_TH_5V0])) { @@ -1487,7 +1490,6 @@ static int config_set(int id, GVariant *data, struct sr_dev_inst *sdi, __func__, devc->th_level); } else if (id == SR_CONF_FILTER) { stropt = g_variant_get_string(data, NULL); - ret = SR_OK; if (!strcmp(stropt, filters[SR_FILTER_NONE])) { devc->filter = SR_FILTER_NONE; } else if (!strcmp(stropt, filters[SR_FILTER_1T])) { @@ -1502,24 +1504,20 @@ static int config_set(int id, GVariant *data, struct sr_dev_inst *sdi, if (sdi->mode == DSO) { ret = command_dso_ctrl(usb->devhdl, dso_cmd_gen(sdi, ch, SR_CONF_EN_CH)); - uint16_t channel_cnt = 0; - GSList *l; - for (l = sdi->channels; l; l = l->next) { - struct sr_channel *probe = (struct sr_channel *)l->data; - channel_cnt += probe->enabled; - } - if (channel_cnt != 0) + if (en_ch_num(sdi) != 0) { ret = command_dso_ctrl(usb->devhdl, dso_cmd_gen(sdi, 0, SR_CONF_SAMPLERATE)); + } } if (ret == SR_OK) sr_dbg("%s: setting ENABLE of channel %d to %d", __func__, ch->index, ch->enabled); else - sr_dbg("%s: setting ENABLE of channel %d to %d", + sr_dbg("%s: setting ENABLE of channel %d to %d failed", __func__, ch->index, ch->enabled); } else if (id == SR_CONF_DATALOCK) { + while(libusb_try_lock_events(drvc->sr_ctx->libusb_ctx)); devc->data_lock = g_variant_get_boolean(data); - ret = SR_OK; + libusb_unlock_events(drvc->sr_ctx->libusb_ctx); } else if (id == SR_CONF_VDIV) { ch->vdiv = g_variant_get_uint64(data); if (sdi->mode == DSO) { @@ -1569,10 +1567,17 @@ static int config_set(int id, GVariant *data, struct sr_dev_inst *sdi, sr_dbg("%s: setting DSO Trigger Slope to %d failed", __func__, devc->trigger_slope); } else if (id == SR_CONF_TRIGGER_SOURCE) { - devc->trigger_source = g_variant_get_byte(data); - if (sdi->mode == DSO) { - ret = command_dso_ctrl(usb->devhdl, dso_cmd_gen(sdi, NULL, SR_CONF_TRIGGER_SOURCE)); - } + devc->trigger_source = (devc->trigger_source & 0xf0) + (g_variant_get_byte(data) & 0x0f); + ret = command_dso_ctrl(usb->devhdl, dso_cmd_gen(sdi, NULL, SR_CONF_TRIGGER_SOURCE)); + if (ret == SR_OK) + sr_dbg("%s: setting DSO Trigger Source to %d", + __func__, devc->trigger_source); + else + sr_dbg("%s: setting DSO Trigger Source to %d failed", + __func__, devc->trigger_source); + } else if (id == SR_CONF_TRIGGER_CHANNEL) { + devc->trigger_source = (g_variant_get_byte(data) << 4) + (devc->trigger_source & 0x0f); + ret = command_dso_ctrl(usb->devhdl, dso_cmd_gen(sdi, NULL, SR_CONF_TRIGGER_SOURCE)); if (ret == SR_OK) sr_dbg("%s: setting DSO Trigger Source to %d", __func__, devc->trigger_source); @@ -1591,18 +1596,14 @@ static int config_set(int id, GVariant *data, struct sr_dev_inst *sdi, sr_dbg("%s: setting DSO Trigger Value to %d failed", __func__, ch->index, ch->trig_value); } else if (id == SR_CONF_HORIZ_TRIGGERPOS) { - uint16_t channel_cnt = 0; - GSList *l; - for (l = sdi->channels; l; l = l->next) { - struct sr_channel *probe = (struct sr_channel *)l->data; - channel_cnt += probe->enabled; - } devc->trigger_hrate = g_variant_get_byte(data); - devc->trigger_hpos = devc->trigger_hrate * channel_cnt * devc->limit_samples / 200.0; - if (sdi->mode == DSO) { - ret = command_dso_ctrl(usb->devhdl, dso_cmd_gen(sdi, 1, SR_CONF_HORIZ_TRIGGERPOS)); - } - if (ret == SR_OK) + //devc->trigger_hpos = devc->trigger_hrate * en_ch_num(sdi) * devc->limit_samples / 200.0; + /* + * devc->trigger_hpos should be updated before each acquisition + * because the samplelimits may changed + */ + devc->trigger_hpos = devc->trigger_hrate * en_ch_num(sdi) * devc->limit_samples / 200.0; + if ((ret = command_dso_ctrl(usb->devhdl, dso_cmd_gen(sdi, 1, SR_CONF_HORIZ_TRIGGERPOS))) == SR_OK) sr_dbg("%s: setting DSO Horiz Trigger Position to %d", __func__, devc->trigger_hpos); else @@ -1656,7 +1657,7 @@ static int config_set(int id, GVariant *data, struct sr_dev_inst *sdi, GSList *l; for(l = sdi->channels; l; l = l->next) { struct sr_channel *probe = (struct sr_channel *)l->data; - if (!dso_load_eep(sdi, probe)) { + if (!dso_load_eep(sdi, probe, FALSE)) { config_set(SR_CONF_ZERO, g_variant_new_boolean(TRUE), sdi, NULL, NULL); sr_info("Zero have not been setted!"); break; @@ -1833,12 +1834,11 @@ static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi, return SR_OK; } -static int dso_init(struct sr_dev_inst *sdi, gboolean from_eep) +static int dso_init(const struct sr_dev_inst *sdi) { int ret, i; GSList *l; struct sr_usb_dev_inst *usb = sdi->conn; - gboolean zeroed = TRUE; for(l = sdi->channels; l; l = l->next) { struct sr_channel *probe = (struct sr_channel *)l->data; @@ -1852,26 +1852,12 @@ static int dso_init(struct sr_dev_inst *sdi, gboolean from_eep) sr_err("Set VDIV of channel %d command failed!", probe->index); return ret; } - if (from_eep && zeroed) { - zeroed = dso_load_eep(sdi, probe); - } ret = command_dso_ctrl(usb->devhdl, dso_cmd_gen(sdi, probe, SR_CONF_VPOS)); if (ret != SR_OK) { sr_err("Set VPOS of channel %d command failed!", probe->index); return ret; } } - if (!zeroed) { - for(l = sdi->channels; l; l = l->next) { - struct sr_channel *probe = (struct sr_channel *)l->data; - for (i = 0; i < 256; i++) { - ret = command_wr_reg(usb->devhdl, i, COMB_ADDR + probe->index*2); - ret = command_wr_reg(usb->devhdl, i, COMB_ADDR + probe->index*2 + 1); - } - } - config_set(SR_CONF_ZERO, g_variant_new_boolean(TRUE), sdi, NULL, NULL); - sr_info("Zero have not been setted!"); - } ret = command_dso_ctrl(usb->devhdl, dso_cmd_gen(sdi, 0, SR_CONF_SAMPLERATE)); if (ret != SR_OK) { @@ -1931,7 +1917,7 @@ static int dso_zero(struct sr_dev_inst *sdi, struct sr_status mstatus) if (devc->zero_stage == -1) { // initialize before zero adjustment - if (dso_init(sdi, 0) == SR_OK) + if (dso_init(sdi) == SR_OK) devc->zero_stage = 0; } else if ((vga_ptr+devc->zero_stage)->key == 0) { ret = SR_OK; @@ -2014,7 +2000,7 @@ static int dso_zero(struct sr_dev_inst *sdi, struct sr_status mstatus) } ret = command_wr_reg(usb->devhdl, 0b0011, COMB_ADDR+6); devc->zero = FALSE; - dso_init(sdi, 0); + dso_init(sdi); } if (ret == SR_OK) @@ -2074,42 +2060,27 @@ static int dev_open(struct sr_dev_inst *sdi) struct DSL_context *devc; int ret; int64_t timediff_us, timediff_ms; - int i; + uint8_t fpga_done; + GSList *l; + gboolean zeroed; devc = sdi->priv; usb = sdi->conn; - /* - * If the firmware was recently uploaded, wait up to MAX_RENUM_DELAY_MS - * milliseconds for the FX2 to renumerate. - */ + /* + * If the firmware was recently uploaded, no dev_open operation should be called. + * Just wait for renumerate -> detach -> attach + */ ret = SR_ERR; if (devc->fw_updated > 0) { - sr_info("Waiting for device to reset."); - /* Takes >= 300ms for the FX2 to be gone from the USB bus. */ - g_usleep(300 * 1000); - timediff_ms = 0; - while (timediff_ms < MAX_RENUM_DELAY_MS) { - if ((ret = DSCope_dev_open(sdi)) == SR_OK) - break; - g_usleep(100 * 1000); - - timediff_us = g_get_monotonic_time() - devc->fw_updated; - timediff_ms = timediff_us / 1000; - sr_spew("Waited %" PRIi64 "ms.", timediff_ms); - } - if (ret != SR_OK) { - sr_err("Device failed to renumerate."); - return SR_ERR; - } - sr_info("Device came back after %" PRIi64 "ms.", timediff_ms); + return SR_ERR; } else { - sr_info("Firmware upload was not needed."); + sr_info("%s: Firmware upload was not needed.", __func__); ret = DSCope_dev_open(sdi); } if (ret != SR_OK) { - sr_err("Unable to open device."); + sr_err("%s: Unable to open device.", __func__); return SR_ERR; } @@ -2117,40 +2088,61 @@ static int dev_open(struct sr_dev_inst *sdi) if (ret != 0) { switch(ret) { case LIBUSB_ERROR_BUSY: - sr_err("Unable to claim USB interface. Another " - "program or driver has already claimed it."); + sr_err("%s: Unable to claim USB interface. Another " + "program or driver has already claimed it.", __func__); break; case LIBUSB_ERROR_NO_DEVICE: - sr_err("Device has been disconnected."); + sr_err("%s: Device has been disconnected.", __func__); break; default: - sr_err("Unable to claim interface: %s.", - libusb_error_name(ret)); + sr_err("%s: Unable to claim interface: %s.", + __func__, libusb_error_name(ret)); break; } return SR_ERR; } - if ((ret = command_fpga_config(usb->devhdl)) != SR_OK) { - sr_err("Send FPGA configure command failed!"); - } else { - /* Takes >= 10ms for the FX2 to be ready for FPGA configure. */ - g_usleep(10 * 1000); - char *fpga_bit = malloc(strlen(DS_RES_PATH)+strlen(devc->profile->fpga_bit33)+1); - if (fpga_bit == NULL) - return SR_ERR_MALLOC; - strcpy(fpga_bit, DS_RES_PATH); - strcat(fpga_bit, devc->profile->fpga_bit33); - ret = fpga_config(usb->devhdl, fpga_bit); - if (ret != SR_OK) { - sr_err("Configure FPGA failed!"); - } - g_free(fpga_bit); + ret = command_get_fpga_done(usb->devhdl, &fpga_done); + if (ret != SR_OK) { + sr_err("Failed to get fpga done infos."); + return SR_ERR; } - if (sdi->mode == DSO) - dso_init(sdi, 1); + if (fpga_done == 0) { + if ((ret = command_fpga_config(usb->devhdl)) != SR_OK) { + sr_err("%s: Send FPGA configure command failed!", __func__); + } else { + /* Takes >= 10ms for the FX2 to be ready for FPGA configure. */ + g_usleep(10 * 1000); + char *fpga_bit = malloc(strlen(DS_RES_PATH)+strlen(devc->profile->fpga_bit33)+1); + if (fpga_bit == NULL) + return SR_ERR_MALLOC; + strcpy(fpga_bit, DS_RES_PATH); + strcat(fpga_bit, devc->profile->fpga_bit33); + ret = fpga_config(usb->devhdl, fpga_bit); + if (ret != SR_OK) { + sr_err("%s: Configure FPGA failed!", __func__); + } + g_free(fpga_bit); + } + } + + // load zero informations + if (sdi->mode == DSO) { + for(l = sdi->channels; l; l = l->next) { + struct sr_channel *probe = (struct sr_channel *)l->data; + zeroed = dso_load_eep(sdi, probe, fpga_done); + if (!zeroed) + break; + } + if (!zeroed) { + config_set(SR_CONF_ZERO, g_variant_new_boolean(TRUE), sdi, NULL, NULL); + sr_info("Zero have not been setted!"); + } + if (fpga_done == 0) + dso_init(sdi); + } return SR_OK; } @@ -2189,53 +2181,31 @@ static int cleanup(void) return ret; } - -static void abort_acquisition(struct DSL_context *devc) +static void remove_sources(struct DSL_context *devc) { int i; - int ret; - struct sr_usb_dev_inst *usb; - - devc->num_samples = -1; - - sr_info("%s: Stopping", __func__); - - /* Stop GPIF acquisition */ - usb = ((struct sr_dev_inst *)devc->cb_data)->conn; - if ((ret = command_stop_acquisition (usb->devhdl)) != SR_OK) - sr_err("Stop DSCope acquisition failed!"); - else - sr_info("Stop DSCope acquisition!"); - - /* Cancel exist transfers */ - if (devc->num_transfers) - for (i = devc->num_transfers - 1; i >= 0; i--) { - if (devc->transfers[i]) - libusb_cancel_transfer(devc->transfers[i]); - } + sr_err("%s: remove fds from polling", __func__); + /* Remove fds from polling. */ + for (i = 0; devc->usbfd[i] != -1; i++) + sr_source_remove(devc->usbfd[i]); + g_free(devc->usbfd); } static void finish_acquisition(struct DSL_context *devc) { struct sr_datafeed_packet packet; - int i, ret; - struct sr_usb_dev_inst *usb; - sr_err("finish acquisition: send SR_DF_END packet"); + sr_err("%s: send SR_DF_END packet", __func__); /* Terminate session. */ packet.type = SR_DF_END; sr_session_send(devc->cb_data, &packet); - - sr_err("finish acquisition: remove fds from polling"); - /* Remove fds from polling. */ - for (i = 0; devc->usbfd[i] != -1; i++) - sr_source_remove(devc->usbfd[i]); - g_free(devc->usbfd); if (devc->num_transfers != 0) { devc->num_transfers = 0; g_free(devc->transfers); } + + devc->status = DSL_FINISH; } static void free_transfer(struct libusb_transfer *transfer) @@ -2243,7 +2213,7 @@ static void free_transfer(struct libusb_transfer *transfer) struct DSL_context *devc; unsigned int i; - devc = transfer->user_data; + devc = transfer->user_data; g_free(transfer->buffer); transfer->buffer = NULL; @@ -2296,42 +2266,32 @@ static void receive_transfer(struct libusb_transfer *transfer) struct sr_datafeed_logic logic; struct sr_datafeed_dso dso; struct sr_datafeed_analog analog; - struct sr_datafeed_meta meta; - struct DSL_context *devc; - int trigger_offset, i, sample_width, cur_sample_count; + + int trigger_offset, i; int trigger_offset_bytes; - uint8_t *cur_buf; - //GTimeVal cur_time; - //g_get_current_time(&cur_time); - //sr_info("receive_transfer: current time %d sec %d usec", cur_time.tv_sec, cur_time.tv_usec); + const uint8_t *cur_buf = transfer->buffer; + struct DSL_context *devc = transfer->user_data; + struct sr_dev_inst *sdi = devc->cb_data; + const int sample_width = 2; + int cur_sample_count = transfer->actual_length / sample_width; - - devc = transfer->user_data; - - /* - * If acquisition has already ended, just free any queued up - * transfer that come in. - */ - if (devc->num_samples == -1) { - free_transfer(transfer); + if (devc->data_lock) { + resubmit_transfer(transfer); return; } + if (devc->abort) + devc->status = DSL_STOP; + sr_info("receive_transfer(): status %d; timeout %d; received %d bytes.", transfer->status, transfer->timeout, transfer->actual_length); - /* Save incoming transfer before reusing the transfer struct. */ - cur_buf = transfer->buffer; - sample_width = 2; - cur_sample_count = transfer->actual_length / sample_width; - switch (transfer->status) { case LIBUSB_TRANSFER_NO_DEVICE: - //abort_acquisition(devc); - free_transfer(transfer); + case LIBUSB_TRANSFER_CANCELLED: devc->status = DSL_ERROR; - return; + break; case LIBUSB_TRANSFER_COMPLETED: case LIBUSB_TRANSFER_TIMED_OUT: /* We may have received some data though. */ break; @@ -2340,30 +2300,20 @@ static void receive_transfer(struct libusb_transfer *transfer) break; } - if (transfer->actual_length == 0 || - packet_has_error || - devc->data_lock) { + if (devc->status == DSL_DATA && + (transfer->actual_length == 0 || + packet_has_error)) { devc->empty_transfer_count++; if (devc->empty_transfer_count > MAX_EMPTY_TRANSFERS) { - /* - * The FX2 gave up. End the acquisition, the frontend - * will work out that the samplecount is short. - */ - //abort_acquisition(devc); - free_transfer(transfer); devc->status = DSL_ERROR; - } else { - resubmit_transfer(transfer); } - return; } else { devc->empty_transfer_count = 0; } trigger_offset = 0; - if (devc->trigger_stage >= 0) { + if (devc->status == DSL_DATA && devc->trigger_stage >= 0) { for (i = 0; i < cur_sample_count; i++) { - const uint16_t cur_sample = devc->sample_wide ? *((const uint16_t*)cur_buf + i) : *((const uint8_t*)cur_buf + i); @@ -2385,7 +2335,7 @@ static void receive_transfer(struct libusb_transfer *transfer) */ packet.type = SR_DF_TRIGGER; packet.payload = NULL; - sr_session_send(devc->cb_data, &packet); + sr_session_send(sdi, &packet); /* * Send the samples that triggered it, @@ -2396,7 +2346,7 @@ static void receive_transfer(struct libusb_transfer *transfer) logic.unitsize = sizeof(*devc->trigger_buffer); logic.length = devc->trigger_stage * logic.unitsize; logic.data = devc->trigger_buffer; - sr_session_send(devc->cb_data, &packet); + sr_session_send(sdi, &packet); devc->trigger_stage = TRIGGER_FIRED; break; @@ -2418,33 +2368,21 @@ static void receive_transfer(struct libusb_transfer *transfer) } } - if (devc->trigger_stage == TRIGGER_FIRED) { + if (devc->status == DSL_DATA && devc->trigger_stage == TRIGGER_FIRED) { /* Send the incoming transfer to the session bus. */ trigger_offset_bytes = trigger_offset * sample_width; // check packet type - if ((*(struct sr_dev_inst *)(devc->cb_data)).mode == LOGIC) { + if (sdi->mode == LOGIC) { packet.type = SR_DF_LOGIC; packet.payload = &logic; logic.length = transfer->actual_length - trigger_offset_bytes; logic.unitsize = sample_width; logic.data_error = 0; logic.data = cur_buf + trigger_offset_bytes; - } else if ((*(struct sr_dev_inst *)(devc->cb_data)).mode == DSO) { - uint16_t channel_cnt = 0; - uint16_t channel_en_cnt = 0; - GSList *l; - int ret; - struct sr_dev_inst *sdi = devc->cb_data; - for (l = sdi->channels; l; l = l->next) { - struct sr_channel *probe = (struct sr_channel *)l->data; - channel_cnt++; - channel_en_cnt += probe->enabled; - } - if (channel_en_cnt == 0) - channel_en_cnt = 1; - + } else if (sdi->mode == DSO) { if (!devc->instant) { - const uint32_t mstatus_offset = devc->limit_samples / (channel_cnt/channel_en_cnt); + const uint32_t mstatus_offset = devc->limit_samples / (g_slist_length(sdi->channels)/en_ch_num(sdi)); + mstatus.pkt_id = *((const uint16_t*)cur_buf + mstatus_offset); mstatus.ch0_max = *((const uint8_t*)cur_buf + mstatus_offset*2 + 1*2); mstatus.ch0_min = *((const uint8_t*)cur_buf + mstatus_offset*2 + 3); mstatus.ch0_period = *((const uint32_t*)cur_buf + mstatus_offset/2 + 2/2); @@ -2457,38 +2395,42 @@ static void receive_transfer(struct libusb_transfer *transfer) mstatus.ch1_pcnt = *((const uint32_t*)cur_buf + mstatus_offset/2 + 14/2); mstatus.vlen = *((const uint32_t*)cur_buf + mstatus_offset/2 + 16/2) & 0x7fffffff; mstatus.stream_mode = *((const uint32_t*)cur_buf + mstatus_offset/2 + 16/2) & 0x80000000; - mstatus.sample_divider = *((const uint32_t*)cur_buf + mstatus_offset/2 + 18/2) & 0x7fffffff; + mstatus.sample_divider = *((const uint32_t*)cur_buf + mstatus_offset/2 + 18/2) & 0x0fffffff; mstatus.sample_divider_tog = *((const uint32_t*)cur_buf + mstatus_offset/2 + 18/2) & 0x80000000; + mstatus.trig_flag = *((const uint32_t*)cur_buf + mstatus_offset/2 + 18/2) & 0x40000000; } else { mstatus.vlen = instant_buffer_size; } - const uint32_t divider = devc->zero ? 0x1 : (uint32_t)ceil(DSCOPE_MAX_SAMPLERATE * 1.0 / devc->cur_samplerate / channel_en_cnt); - if ((mstatus.sample_divider == divider && - mstatus.vlen != 0 && - mstatus.vlen <= (transfer->actual_length - 512) / sample_width) || + const uint32_t divider = devc->zero ? 0x1 : (uint32_t)ceil(DSCOPE_MAX_SAMPLERATE * 1.0 / devc->cur_samplerate / en_ch_num(sdi)); + if ((mstatus.pkt_id == DSO_PKTID && + mstatus.sample_divider == divider && + mstatus.vlen != 0 && + mstatus.vlen <= (transfer->actual_length - 512) / sample_width) || devc->instant) { - mstatus_valid = devc->instant ? FALSE : TRUE; + devc->stream = (mstatus.stream_mode != 0); + devc->mstatus_valid = TRUE; packet.type = SR_DF_DSO; packet.payload = &dso; - dso.probes = (*(struct sr_dev_inst *)(devc->cb_data)).channels; + dso.probes = sdi->channels; //dso.num_samples = (transfer->actual_length - 512) / sample_width; - cur_sample_count = 2 * mstatus.vlen / channel_en_cnt ; + cur_sample_count = 2 * mstatus.vlen / en_ch_num(sdi) ; dso.num_samples = cur_sample_count; dso.mq = SR_MQ_VOLTAGE; dso.unit = SR_UNIT_VOLT; dso.mqflags = SR_MQFLAG_AC; - dso.samplerate_tog = mstatus.sample_divider_tog; + dso.samplerate_tog = (mstatus.sample_divider_tog != 0); + dso.trig_flag = (mstatus.trig_flag != 0); dso.data = cur_buf + trigger_offset_bytes; } else { packet.type = SR_DF_ABANDON; - mstatus_valid = FALSE; + devc->mstatus_valid = FALSE; } } else { packet.type = SR_DF_ANALOG; packet.payload = &analog; - analog.probes = (*(struct sr_dev_inst *)(devc->cb_data)).channels; - analog.num_samples = transfer->actual_length / sample_width; + analog.probes = sdi->channels; + analog.num_samples = (transfer->actual_length / sample_width)/g_slist_length(analog.probes); analog.mq = SR_MQ_VOLTAGE; analog.unit = SR_UNIT_VOLT; analog.mqflags = SR_MQFLAG_AC; @@ -2513,6 +2455,7 @@ static void receive_transfer(struct libusb_transfer *transfer) } if (cur_sample != test_sample_value) { logic.data_error = 1; + sr_err("exp: %d; act: %d", test_sample_value, cur_sample); break; } test_sample_value++; @@ -2537,26 +2480,21 @@ static void receive_transfer(struct libusb_transfer *transfer) /* send data to session bus */ if (packet.type != SR_DF_ABANDON) - sr_session_send(devc->cb_data, &packet); + sr_session_send(sdi, &packet); } devc->num_samples += cur_sample_count; - if (((*(struct sr_dev_inst *)(devc->cb_data)).mode == LOGIC || devc->instant) && + if ((sdi->mode == LOGIC || devc->instant) && devc->limit_samples && (unsigned int)devc->num_samples >= devc->limit_samples) { - //abort_acquisition(devc); - free_transfer(transfer); devc->status = DSL_STOP; - return; } - } else { - /* - * TODO: Buffer pre-trigger data in capture - * ratio-sized buffer. - */ } - resubmit_transfer(transfer); + if (devc->status == DSL_DATA) + resubmit_transfer(transfer); + else + free_transfer(transfer); } static unsigned int to_bytes_per_ms(struct DSL_context *devc) @@ -2631,31 +2569,23 @@ static int dev_transfer_start(const struct sr_dev_inst *sdi) #else num_transfers = buffer_cnt; #endif - uint16_t channel_en_cnt = 0; - uint16_t channel_cnt = 0; - GSList *l; - for (l = sdi->channels; l; l = l->next) { - struct sr_channel *probe = (struct sr_channel *)l->data; - channel_en_cnt += probe->enabled; - channel_cnt++; - } + if (devc->instant) - dso_buffer_size = instant_buffer_size * channel_cnt; + dso_buffer_size = instant_buffer_size * g_slist_length(sdi->channels); else - dso_buffer_size = devc->limit_samples * channel_en_cnt + 512; + dso_buffer_size = devc->limit_samples * en_ch_num(sdi) + 512; size = (sdi->mode == ANALOG) ? cons_buffer_size : ((sdi->mode == DSO) ? dso_buffer_size : buffer_size); devc->submitted_transfers = 0; devc->transfers = g_try_malloc0(sizeof(*devc->transfers) * num_transfers); if (!devc->transfers) { - sr_err("USB transfers malloc failed."); + sr_err("%s: USB transfers malloc failed.", __func__); return SR_ERR_MALLOC; } - devc->num_transfers = num_transfers; for (i = 0; i < num_transfers; i++) { if (!(buf = g_try_malloc(size))) { - sr_err("USB transfer buffer malloc failed."); + sr_err("%s: USB transfer buffer malloc failed.", __func__); return SR_ERR_MALLOC; } transfer = libusb_alloc_transfer(0); @@ -2663,15 +2593,17 @@ static int dev_transfer_start(const struct sr_dev_inst *sdi) 6 | LIBUSB_ENDPOINT_IN, buf, size, receive_transfer, devc, 0); if ((ret = libusb_submit_transfer(transfer)) != 0) { - sr_err("Failed to submit transfer: %s.", - libusb_error_name(ret)); + sr_err("%s: Failed to submit transfer: %s.", + __func__, libusb_error_name(ret)); libusb_free_transfer(transfer); g_free(buf); - abort_acquisition(devc); + devc->status = DSL_ERROR; + devc->abort = TRUE; return SR_ERR; } devc->transfers[i] = transfer; devc->submitted_transfers++; + devc->num_transfers++; } devc->status = DSL_DATA; @@ -2686,18 +2618,16 @@ static int receive_data(int fd, int revents, const struct sr_dev_inst *sdi) struct timeval tv; struct drv_context *drvc; struct DSL_context *devc; + struct sr_usb_dev_inst *usb; + int i; + int ret; (void)fd; (void)revents; drvc = di->priv; devc = sdi->priv; - - if (devc->num_samples != -1 && - (devc->status == DSL_STOP || devc->status == DSL_ERROR)) { - sr_info("%s: Stopping", __func__); - abort_acquisition(devc); - } + usb = sdi->conn; tv.tv_sec = tv.tv_usec = 0; libusb_handle_events_timeout_completed(drvc->sr_ctx->libusb_ctx, &tv, &completed); @@ -2706,6 +2636,22 @@ static int receive_data(int fd, int revents, const struct sr_dev_inst *sdi) dso_zero(sdi, mstatus); } + if (devc->status == DSL_FINISH) { + if (libusb_try_lock_events(drvc->sr_ctx->libusb_ctx) == 0) { + if (libusb_event_handling_ok(drvc->sr_ctx->libusb_ctx)) { + /* Stop GPIF acquisition */ + usb = ((struct sr_dev_inst *)devc->cb_data)->conn; + if ((ret = command_stop_acquisition (usb->devhdl)) != SR_OK) + sr_err("%s: Sent acquisition stop command failed!", __func__); + else + sr_info("%s: Sent acquisition stop command!", __func__); + + remove_sources(devc); + } + libusb_unlock_events(drvc->sr_ctx->libusb_ctx); + } + } + return TRUE; } @@ -2713,67 +2659,42 @@ static void receive_trigger_pos(struct libusb_transfer *transfer) { struct DSL_context *devc; struct sr_datafeed_packet packet; - struct sr_datafeed_logic logic; - struct sr_datafeed_dso dso; - struct sr_datafeed_analog analog; struct ds_trigger_pos *trigger_pos; + const struct sr_dev_inst *sdi; int ret; devc = transfer->user_data; - sr_info("receive_trigger_pos(): status %d; timeout %d; received %d bytes.", - transfer->status, transfer->timeout, transfer->actual_length); - - if (devc->num_samples == -1) { - free_transfer(transfer); - return; - } - + sdi = devc->cb_data; trigger_pos = (struct ds_trigger_pos *)transfer->buffer; - switch (transfer->status) { - case LIBUSB_TRANSFER_COMPLETED: + devc->status = DSL_ERROR; + if (transfer->status == LIBUSB_TRANSFER_COMPLETED && + trigger_pos->check_id == TRIG_CHECKID) { + sr_info("receive_trigger_pos(): status %d; timeout %d; received %d bytes.", + transfer->status, transfer->timeout, transfer->actual_length); if (transfer->actual_length == sizeof(struct ds_trigger_pos)) { packet.type = SR_DF_TRIGGER; packet.payload = trigger_pos; - sr_session_send(devc->cb_data, &packet); + sr_session_send(sdi, &packet); devc->status = DSL_TRIGGERED; - free_transfer(transfer); devc->num_transfers = 0; devc->empty_transfer_count = 0; - } else { - free_transfer(transfer); - devc->status = DSL_ERROR; } - break; - case LIBUSB_TRANSFER_TIMED_OUT: - devc->empty_transfer_count++; - if (devc->empty_transfer_count > MAX_EMPTY_TRANSFERS) { - /* - * The FX2 gave up. End the acquisition, the frontend - * will work out that the samplecount is short. - */ - //abort_acquisition(devc); - free_transfer(transfer); - devc->status = DSL_ERROR; - } else { - resubmit_transfer(transfer); - } - break; - case LIBUSB_TRANSFER_CANCELLED: - resubmit_transfer(transfer); - break; - default: - //abort_acquisition(devc); - free_transfer(transfer); - devc->status = DSL_ERROR; - break; } if (devc->status == DSL_TRIGGERED) { + // successfull + free_transfer(transfer); if ((ret = dev_transfer_start(devc->cb_data)) != SR_OK) { sr_err("%s: could not start data transfer" "(%d)%d", __func__, ret, errno); } + } else if (devc->status == DSL_START) { + // retry + resubmit_transfer(transfer); + } else { + // failed + free_transfer(transfer); } } @@ -2787,9 +2708,6 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data) const struct libusb_pollfd **lupfd; unsigned int i; int ret; - int transferred; - struct sr_datafeed_packet packet; - int header_transferred; test_init = 1; @@ -2807,57 +2725,82 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data) devc->status = DSL_INIT; devc->num_transfers = 0; devc->submitted_transfers = 0; + test_sample_value = 0; + devc->abort = FALSE; /* Configures devc->trigger_* and devc->sample_wide */ if (configure_probes(sdi) != SR_OK) { - sr_err("Failed to configure probes."); + sr_err("%s: Failed to configure probes.", __func__); return SR_ERR; } /* Stop Previous GPIF acquisition */ if ((ret = command_stop_acquisition (usb->devhdl)) != SR_OK) { - sr_err("Stop DSCope acquisition failed!"); - abort_acquisition(devc); + sr_err("%s: Stop DSCope acquisition failed!", __func__); return ret; } else { - sr_info("Stop Previous DSCope acquisition!"); + sr_info("%s: Stop Previous DSCope acquisition!", __func__); } /* Setting FPGA before acquisition start*/ if ((ret = command_fpga_setting(usb->devhdl, sizeof(struct DSL_setting) / sizeof(uint16_t))) != SR_OK) { - sr_err("Send FPGA setting command failed!"); + sr_err("%s: Send FPGA setting command failed!", __func__); } else { if ((ret = fpga_setting(sdi)) != SR_OK) { - sr_err("Configure FPGA failed!"); - abort_acquisition(devc); + sr_err("%s: Configure FPGA failed!", __func__); return ret; } } -// if (sdi->mode == DSO) { -// GSList *l; -// for(l = sdi->channels; l; l = l->next) { -// struct sr_channel *probe = (struct sr_channel *)l->data; -// ret = command_dso_ctrl(usb->devhdl, dso_cmd_gen(sdi, probe, SR_CONF_COUPLING)); -// if (ret != SR_OK) { -// sr_err("Set COUPLING of channel %d command failed!", probe->index); -// return ret; -// } -// ret = command_dso_ctrl(usb->devhdl, dso_cmd_gen(sdi, probe, SR_CONF_VPOS)); -// if (ret != SR_OK) { -// sr_err("Set VDIV of channel %d command failed!", probe->index); -// return ret; -// } -// } -// } - - if ((ret = command_start_acquisition (usb->devhdl, - devc->cur_samplerate, devc->sample_wide, (sdi->mode == LOGIC))) != SR_OK) { - abort_acquisition(devc); - return ret; + if (devc->zero && devc->zero_stage == -1) { + // initialize before zero adjustment + if ((ret = dso_init(sdi)) == SR_OK) { + devc->zero_stage = 0; + } else { + sr_err("%s: DSO zero initialization failed!", __func__); + return ret; + } + devc->zero_stage = 0; } - test_sample_value = 0; + /* + * settings must be updated before acquisition + */ + if (sdi->mode == DSO) { + devc->trigger_hpos = devc->trigger_hrate * en_ch_num(sdi) * devc->limit_samples / 200.0; + if ((ret = command_dso_ctrl(usb->devhdl, dso_cmd_gen(sdi, 1, SR_CONF_HORIZ_TRIGGERPOS))) == SR_OK) + sr_dbg("%s: setting DSO Horiz Trigger Position to %d", + __func__, devc->trigger_hpos); + else + sr_dbg("%s: setting DSO Horiz Trigger Position to %d failed", + __func__, devc->trigger_hpos); + } + + /* poll trigger status transfer*/ + if (!(trigger_pos = g_try_malloc0(sizeof(struct ds_trigger_pos)))) { + sr_err("%s: USB trigger_pos buffer malloc failed.", __func__); + return SR_ERR_MALLOC; + } + devc->transfers = g_try_malloc0(sizeof(*devc->transfers)); + if (!devc->transfers) { + sr_err("%s: USB trigger_pos transfer malloc failed.", __func__); + return SR_ERR_MALLOC; + } + transfer = libusb_alloc_transfer(0); + libusb_fill_bulk_transfer(transfer, usb->devhdl, + 6 | LIBUSB_ENDPOINT_IN, trigger_pos, sizeof(struct ds_trigger_pos), + receive_trigger_pos, devc, 0); + if ((ret = libusb_submit_transfer(transfer)) != 0) { + sr_err("%s: Failed to submit trigger_pos transfer: %s.", + __func__, libusb_error_name(ret)); + libusb_free_transfer(transfer); + g_free(trigger_pos); + return SR_ERR; + } else { + devc->num_transfers++; + devc->transfers[0] = transfer; + devc->submitted_transfers++; + } /* setup callback function for data transfer */ lupfd = libusb_get_pollfds(drvc->sr_ctx->libusb_ctx); @@ -2866,46 +2809,21 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data) return SR_ERR; for (i = 0; lupfd[i]; i++) { sr_source_add(lupfd[i]->fd, lupfd[i]->events, - get_timeout(devc), receive_data, sdi); - devc->usbfd[i] = lupfd[i]->fd; + get_timeout(devc), receive_data, sdi); + devc->usbfd[i] = lupfd[i]->fd; } devc->usbfd[i] = -1; free(lupfd); - - /* poll trigger status transfer*/ - if (!(trigger_pos = g_try_malloc0(sizeof(struct ds_trigger_pos)))) { - sr_err("USB trigger_pos buffer malloc failed."); - return SR_ERR_MALLOC; - } - devc->transfers = g_try_malloc0(sizeof(*devc->transfers)); - if (!devc->transfers) { - sr_err("USB trigger_pos transfer malloc failed."); - return SR_ERR_MALLOC; - } - devc->num_transfers = 1; - transfer = libusb_alloc_transfer(0); - libusb_fill_bulk_transfer(transfer, usb->devhdl, - 6 | LIBUSB_ENDPOINT_IN, trigger_pos, sizeof(struct ds_trigger_pos), - receive_trigger_pos, devc, 0); - if ((ret = libusb_submit_transfer(transfer)) != 0) { - sr_err("Failed to submit trigger_pos transfer: %s.", - libusb_error_name(ret)); - libusb_free_transfer(transfer); - g_free(trigger_pos); - abort_acquisition(devc); - return SR_ERR; - } - devc->transfers[0] = transfer; - devc->submitted_transfers++; - - if (devc->zero && devc->zero_stage == -1) { - // initialize before zero adjustment - if (dso_init(sdi, 0) == SR_OK) - devc->zero_stage = 0; - } devc->status = DSL_START; - mstatus_valid = FALSE; + devc->mstatus_valid = FALSE; + if ((ret = command_start_acquisition (usb->devhdl, + devc->cur_samplerate, devc->sample_wide, (sdi->mode == LOGIC))) != SR_OK) { + devc->status = DSL_ERROR; + devc->abort = TRUE; + return ret; + } + /* Send header packet to the session bus. */ //std_session_send_df_header(cb_data, LOG_PREFIX); std_session_send_df_header(sdi, LOG_PREFIX); @@ -2917,12 +2835,18 @@ static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data) { (void)cb_data; + struct drv_context *drvc; struct DSL_context *devc; + struct sr_usb_dev_inst *usb; + drvc = di->priv; devc = sdi->priv; - devc->status = DSL_STOP; + usb = sdi->conn; - //abort_acquisition(sdi->priv); + if (!devc->abort) { + devc->abort = TRUE; + command_wr_reg(usb->devhdl, 3, EEWP_ADDR); + } return SR_OK; } @@ -2949,20 +2873,22 @@ static int dev_test(struct sr_dev_inst *sdi) static int dev_status_get(struct sr_dev_inst *sdi, struct sr_status *status, int begin, int end) { - (void)begin; - (void)end; + int ret = SR_ERR; if (sdi) { struct DSL_context *devc; + struct sr_usb_dev_inst *usb; + devc = sdi->priv; - if (mstatus_valid) { + usb = sdi->conn; + if (devc->status == DSL_START) { + ret = command_get_status(usb->devhdl, (unsigned char*)status, begin, end); + } else if (devc->mstatus_valid) { *status = mstatus; - return SR_OK; - } else { - return SR_ERR; + ret = SR_OK; } - } else { - return SR_ERR; } + + return ret; } SR_PRIV struct sr_dev_driver DSCope_driver_info = { diff --git a/libsigrok4DSL/hardware/DSL/dsl.h b/libsigrok4DSL/hardware/DSL/dsl.h index dcc94f2a..6fa3061b 100644 --- a/libsigrok4DSL/hardware/DSL/dsl.h +++ b/libsigrok4DSL/hardware/DSL/dsl.h @@ -121,6 +121,11 @@ #define CALI_VGAIN_RANGE 100 #define CALI_VOFF_RANGE (1024-DSCOPE20_DEFAULT_TRANS) +#define DSO_AUTOTRIG_THRESHOLD 16 + +#define TRIG_CHECKID 0xa500005a +#define DSO_PKTID 0xa500 + struct DSL_profile { uint16_t vid; uint16_t pid; @@ -175,6 +180,17 @@ static const struct DSL_profile supported_DSCope[3] = { { 0, 0, 0, 0, 0, 0, 0, 0, 0 } }; +static const gboolean default_ms_en[DSO_MS_END - DSO_MS_BEGIN] = { + FALSE, /* DSO_MS_BEGIN */ + TRUE, /* DSO_MS_FREQ */ + FALSE, /* DSO_MS_PERD */ + TRUE, /* DSO_MS_VMAX */ + TRUE, /* DSO_MS_VMIN */ + FALSE, /* DSO_MS_VRMS */ + FALSE, /* DSO_MS_VMEA */ + FALSE, /* DSO_MS_VP2P */ + FALSE, /* DSO_MS_END */ +}; enum { DSL_ERROR = -1, @@ -184,6 +200,7 @@ enum { DSL_TRIGGERED = 3, DSL_DATA = 4, DSL_STOP = 5, + DSL_FINISH = 6, }; struct DSL_context { @@ -210,6 +227,7 @@ struct DSL_context { uint16_t op_mode; uint16_t ch_mode; uint16_t samplerates_size; + uint16_t samplecounts_size; uint16_t th_level; double vth; uint16_t filter; @@ -219,6 +237,7 @@ struct DSL_context { uint16_t trigger_buffer[NUM_TRIGGER_STAGES]; uint64_t timebase; uint8_t max_height; + uint8_t trigger_channel; uint8_t trigger_slope; uint8_t trigger_source; uint8_t trigger_hrate; @@ -235,7 +254,6 @@ struct DSL_context { uint8_t dso_bits; int num_samples; - uint64_t sent_samples; int submitted_transfers; int empty_transfer_count; @@ -249,6 +267,7 @@ struct DSL_context { int status; gboolean mstatus_valid; + gboolean abort; }; struct DSL_setting { diff --git a/libsigrok4DSL/hardware/DSL/dslogic.c b/libsigrok4DSL/hardware/DSL/dslogic.c index 3cf3a7e8..503ecbf8 100644 --- a/libsigrok4DSL/hardware/DSL/dslogic.c +++ b/libsigrok4DSL/hardware/DSL/dslogic.c @@ -160,6 +160,7 @@ static const int32_t sessions_pro[] = { SR_CONF_FILTER, SR_CONF_TRIGGER_SLOPE, SR_CONF_TRIGGER_SOURCE, + SR_CONF_TRIGGER_CHANNEL, SR_CONF_HORIZ_TRIGGERPOS, SR_CONF_TRIGGER_HOLDOFF, SR_CONF_TRIGGER_MARGIN, @@ -238,7 +239,6 @@ static const uint64_t samplecounts[] = { }; static const uint8_t zero_base_addr = 0x80; -static const uint8_t comb_base_addr = 0xB0; SR_PRIV struct sr_dev_driver DSLogic_driver_info; static struct sr_dev_driver *di = &DSLogic_driver_info; @@ -246,7 +246,6 @@ static struct sr_dev_driver *di = &DSLogic_driver_info; extern struct ds_trigger *trigger; struct sr_status mstatus; -struct cmd_zero_info zero_info; /** * Check the USB configuration to determine if this is an DSLogic device. @@ -292,6 +291,20 @@ static gboolean check_conf_profile(libusb_device *dev) return ret; } +static int en_ch_num(const struct sr_dev_inst *sdi) +{ + GSList *l; + int channel_en_cnt = 0; + + for (l = sdi->channels; l; l = l->next) { + struct sr_channel *probe = (struct sr_channel *)l->data; + channel_en_cnt += probe->enabled; + } + channel_en_cnt += (channel_en_cnt == 0); + + return channel_en_cnt; +} + static int fpga_setting(const struct sr_dev_inst *sdi) { struct DSL_context *devc; @@ -340,14 +353,6 @@ static int fpga_setting(const struct sr_dev_inst *sdi) //setting.trig_logic3_header = 0x2310ffff; setting.end_sync = 0xfa5afa5a; - for (l = sdi->channels; l; l = l->next) { - struct sr_channel *probe = (struct sr_channel *)l->data; - channel_en_cnt += probe->enabled; - channel_cnt++; - } - if (channel_en_cnt == 0) - channel_en_cnt = 1; - devc->rle_mode = FALSE; if (sdi->mode == LOGIC && !devc->stream && @@ -361,18 +366,21 @@ static int fpga_setting(const struct sr_dev_inst *sdi) ((devc->stream) << 12) + ((trigger->trigger_mode == SERIAL_TRIGGER) << 11) + trigger->trigger_en + - ((sdi->mode > 0) << 4) + (devc->clock_type << 1) + (devc->clock_edge << 2) + + ((sdi->mode == DSO) << 4) + (devc->clock_type << 1) + (devc->clock_edge << 2) + (devc->rle_mode << 3) + ((((devc->cur_samplerate == (2 * DSLOGIC_MAX_LOGIC_SAMPLERATE)) && sdi->mode != DSO) || (sdi->mode == ANALOG)) << 5) + ((devc->cur_samplerate == (4 * DSLOGIC_MAX_LOGIC_SAMPLERATE)) << 6) + ((sdi->mode == ANALOG) << 7) + ((devc->filter == SR_FILTER_1T) << 8) + - (devc->instant << 9) + (devc->zero << 10); - if (sdi->mode == DSO) - setting.divider = devc->zero ? 0x1 : (uint32_t)ceil(DSLOGIC_MAX_DSO_SAMPLERATE * 1.0 / devc->cur_samplerate / channel_en_cnt); - else - setting.divider = devc->zero ? 0x1 : (uint32_t)ceil(DSLOGIC_MAX_LOGIC_SAMPLERATE * 1.0 / devc->cur_samplerate); - setting.count = (sdi->mode == DSO) ? (uint32_t)(devc->limit_samples / (channel_cnt / channel_en_cnt)) : (uint32_t)(devc->limit_samples); + (devc->instant << 9); + + setting.divider = (sdi->mode == DSO) ? (uint32_t)ceil(DSLOGIC_MAX_DSO_SAMPLERATE * 1.0 / devc->cur_samplerate / en_ch_num(sdi)) : + (uint32_t)ceil(DSLOGIC_MAX_LOGIC_SAMPLERATE * 1.0 / devc->cur_samplerate); + + // analog: 16bits, but sample with half mode(0-7 valid only) + setting.count = (sdi->mode == DSO) ? (uint32_t)(devc->limit_samples / (g_slist_length(sdi->channels) / en_ch_num(sdi))) : + (sdi->mode == ANALOG) ? (uint32_t)(devc->limit_samples * g_slist_length(sdi->channels) * 4) : + (uint32_t)(devc->limit_samples); setting.trig_pos = (uint32_t)(trigger->trigger_pos / 100.0 * devc->limit_samples); setting.trig_glb = trigger->trigger_stages; setting.trig_adp = setting.count - setting.trig_pos - 1; @@ -431,7 +439,7 @@ static int fpga_setting(const struct sr_dev_inst *sdi) result = SR_OK; ret = libusb_bulk_transfer(hdl, 2 | LIBUSB_ENDPOINT_OUT, &setting, sizeof(struct DSL_setting), - &transferred, 3000); + &transferred, 1000); if (ret < 0) { sr_err("Unable to setting FPGA of DSLogic: %s.", @@ -689,6 +697,7 @@ static struct DSL_context *DSLogic_dev_new(void) devc->op_mode = SR_OP_BUFFER; devc->ch_mode = 0; devc->samplerates_size = 14; + devc->samplecounts_size = ARRAY_SIZE(samplecounts); devc->th_level = SR_TH_3V3; devc->vth = 1.0; devc->filter = SR_FILTER_NONE; @@ -704,6 +713,8 @@ static struct DSL_context *DSLogic_dev_new(void) devc->data_lock = FALSE; devc->max_height = 0; devc->dso_bits = 8; + devc->trigger_margin = 8; + devc->trigger_channel = 0; return devc; } @@ -718,6 +729,25 @@ static int init(struct sr_context *sr_ctx) return std_hw_init(sr_ctx, di, LOG_PREFIX); } +static int probe_init(struct sr_dev_inst *sdi) +{ + int i; + GList *l; + for (l = sdi->channels; l; l = l->next) { + struct sr_channel *probe = (struct sr_channel *)l->data; + if (sdi->mode == DSO) { + probe->vdiv = 1000; + probe->vfactor = 1; + probe->vpos = 0; + probe->coupling = SR_DC_COUPLING; + probe->trig_value = 0x80; + probe->ms_show = TRUE; + for (i = DSO_MS_BEGIN; i < DSO_MS_END; i++) + probe->ms_en[i] = default_ms_en[i]; + } + } +} + static int set_probes(struct sr_dev_inst *sdi, int num_probes) { uint16_t j; @@ -727,15 +757,9 @@ static int set_probes(struct sr_dev_inst *sdi, int num_probes) if (!(probe = sr_channel_new(j, (sdi->mode == LOGIC) ? SR_CHANNEL_LOGIC : ((sdi->mode == DSO) ? SR_CHANNEL_DSO : SR_CHANNEL_ANALOG), TRUE, probe_names[j]))) return SR_ERR; - if (sdi->mode == DSO) { - probe->vdiv = 1000; - probe->vfactor = 1; - probe->vpos = 0; - probe->coupling = SR_DC_COUPLING; - probe->trig_value = 0x80; - } sdi->channels = g_slist_append(sdi->channels, probe); } + probe_init(sdi); return SR_OK; } @@ -847,7 +871,7 @@ static GSList *scan(GSList *options) devc->profile = prof; sdi->priv = devc; drvc->instances = g_slist_append(drvc->instances, sdi); - devices = g_slist_append(devices, sdi); + //devices = g_slist_append(devices, sdi); if (check_conf_profile(devlist[i])) { /* Already has the firmware, so fix the new address. */ @@ -856,6 +880,8 @@ static GSList *scan(GSList *options) sdi->inst_type = SR_INST_USB; sdi->conn = sr_usb_dev_inst_new(libusb_get_bus_number(devlist[i]), libusb_get_device_address(devlist[i]), NULL); + /* only report device after firmware is ready */ + devices = g_slist_append(devices, sdi); } else { char *firmware; if (!(firmware = g_try_malloc(strlen(DS_RES_PATH)+strlen(prof->firmware)+1))) { @@ -905,13 +931,12 @@ static GSList *dev_mode_list(const struct sr_dev_inst *sdi) return l; } -static uint64_t dso_cmd_gen(struct sr_dev_inst *sdi, struct sr_channel* ch, int id) +static uint64_t dso_cmd_gen(const struct sr_dev_inst *sdi, struct sr_channel* ch, int id) { struct DSL_context *devc; uint64_t cmd = 0; int channel_cnt = 0; GSList *l; - const int ch_bit = 7; devc = sdi->priv; @@ -980,7 +1005,7 @@ static uint64_t dso_cmd_gen(struct sr_dev_inst *sdi, struct sr_channel* ch, int channel_cnt += probe->enabled; } cmd += 0x18; - uint32_t divider = devc->zero ? 0x1 : (uint32_t)ceil(DSLOGIC_MAX_DSO_SAMPLERATE * 1.0 / devc->cur_samplerate / channel_cnt); + uint32_t divider = (uint32_t)ceil(DSLOGIC_MAX_DSO_SAMPLERATE * 1.0 / devc->cur_samplerate / channel_cnt); cmd += divider << 8; break; case SR_CONF_HORIZ_TRIGGERPOS: @@ -1014,17 +1039,18 @@ static uint64_t dso_cmd_gen(struct sr_dev_inst *sdi, struct sr_channel* ch, int cmd = 0xa5a5a500; break; default: - cmd = 0x00000000; + cmd = 0xFFFFFFFF; } return cmd; } -static int dso_init(struct sr_dev_inst *sdi) +static int dso_init(const struct sr_dev_inst *sdi, gboolean from_eep) { - int ret; + int ret, i; GSList *l; struct sr_usb_dev_inst *usb = sdi->conn; + gboolean zeroed = FALSE; for(l = sdi->channels; l; l = l->next) { struct sr_channel *probe = (struct sr_channel *)l->data; @@ -1241,7 +1267,13 @@ static int config_get(int id, GVariant **data, const struct sr_dev_inst *sdi, if (!sdi) return SR_ERR; devc = sdi->priv; - *data = g_variant_new_byte(devc->trigger_source); + *data = g_variant_new_byte(devc->trigger_source&0x0f); + break; + case SR_CONF_TRIGGER_CHANNEL: + if (!sdi) + return SR_ERR; + devc = sdi->priv; + *data = g_variant_new_byte(devc->trigger_source>>4); break; case SR_CONF_TRIGGER_VALUE: if (!ch) @@ -1334,15 +1366,18 @@ static int config_set(int id, GVariant *data, struct sr_dev_inst *sdi, int ret, num_probes; struct sr_usb_dev_inst *usb; int i; + struct drv_context *drvc; (void)cg; if (sdi->status != SR_ST_ACTIVE) return SR_ERR; + drvc = di->priv; devc = sdi->priv; usb = sdi->conn; + ret = SR_OK; if (id == SR_CONF_SAMPLERATE) { devc->cur_samplerate = g_variant_get_uint64(data); if(sdi->mode == DSO) { @@ -1350,42 +1385,30 @@ static int config_set(int id, GVariant *data, struct sr_dev_inst *sdi, ret = command_dso_ctrl(usb->devhdl, dso_cmd_gen(sdi, 0, SR_CONF_SAMPLERATE)); } else { devc->sample_wide = (devc->cur_samplerate <= DSLOGIC_MAX_LOGIC_SAMPLERATE); - ret = SR_OK; } } else if (id == SR_CONF_CLOCK_TYPE) { devc->clock_type = g_variant_get_boolean(data); - ret = SR_OK; } else if (id == SR_CONF_CLOCK_EDGE) { devc->clock_edge = g_variant_get_boolean(data); - ret = SR_OK; } else if (id == SR_CONF_RLE) { devc->rle_mode = g_variant_get_boolean(data); - ret = SR_OK; } else if (id == SR_CONF_INSTANT) { if (sdi->mode == DSO) { devc->instant = g_variant_get_boolean(data); - int num_probes = 0; - GSList *l; - for (l = sdi->channels; l; l = l->next) { - struct sr_channel *probe = (struct sr_channel *)l->data; - num_probes += probe->enabled; - } - if (num_probes != 0) { + if (en_ch_num(sdi) != 0) { if (devc->instant) - devc->limit_samples = DSLOGIC_INSTANT_DEPTH / num_probes; + devc->limit_samples = DSLOGIC_INSTANT_DEPTH / en_ch_num(sdi); else - devc->limit_samples = DSLOGIC_MAX_DSO_DEPTH / num_probes; + devc->limit_samples = DSLOGIC_MAX_DSO_DEPTH / en_ch_num(sdi); } } - ret = SR_OK; } else if (id == SR_CONF_LIMIT_SAMPLES) { devc->limit_samples = g_variant_get_uint64(data); - ret = SR_OK; } else if (id == SR_CONF_DEVICE_MODE) { sdi->mode = g_variant_get_int16(data); - ret = SR_OK; if (sdi->mode == LOGIC) { num_probes = devc->profile->dev_caps & DEV_CAPS_16BIT ? 16 : 8; + devc->samplecounts_size = ARRAY_SIZE(samplecounts); } else if (sdi->mode == DSO) { sdi->mode = DSO; num_probes = devc->profile->dev_caps & DEV_CAPS_16BIT ? MAX_DSO_PROBES_NUM : 1; @@ -1394,18 +1417,23 @@ static int config_set(int id, GVariant *data, struct sr_dev_inst *sdi, sr_dbg("%s: DSO configuration sync failed", __func__); devc->cur_samplerate = DSLOGIC_MAX_DSO_SAMPLERATE / num_probes; devc->limit_samples = DSLOGIC_MAX_DSO_DEPTH / num_probes; + devc->samplerates_size = 15; + devc->samplecounts_size = ARRAY_SIZE(samplecounts); } else if (sdi->mode == ANALOG){ num_probes = devc->profile->dev_caps & DEV_CAPS_16BIT ? MAX_ANALOG_PROBES_NUM : 1; + devc->op_mode = SR_OP_STREAM; + devc->stream = TRUE; + devc->samplerates_size = 10; + devc->samplecounts_size = 15; } sr_dev_probes_free(sdi); set_probes(sdi, num_probes); sr_dbg("%s: setting mode to %d", __func__, sdi->mode); if (sdi->mode == DSO) { - dso_init(sdi); + dso_init(sdi, 0); } } else if (id == SR_CONF_OPERATION_MODE) { stropt = g_variant_get_string(data, NULL); - ret = SR_OK; if (sdi->mode == LOGIC) { if (!strcmp(stropt, opmodes[SR_OP_BUFFER]) && (devc->op_mode != SR_OP_BUFFER)) { devc->op_mode = SR_OP_BUFFER; @@ -1453,12 +1481,16 @@ static int config_set(int id, GVariant *data, struct sr_dev_inst *sdi, devc->cur_samplerate = samplerates[devc->samplerates_size-1]; devc->sample_wide = (devc->cur_samplerate <= DSLOGIC_MAX_DSO_SAMPLERATE); } + } else if (sdi->mode == ANALOG) { + devc->op_mode = SR_OP_STREAM; + devc->stream = TRUE; + devc->samplerates_size = 10; + devc->samplecounts_size = 15; } sr_dbg("%s: setting pattern to %d", __func__, devc->op_mode); } else if (id == SR_CONF_CHANNEL_MODE) { stropt = g_variant_get_string(data, NULL); - ret = SR_OK; if (sdi->mode == LOGIC) { if (devc->stream) { for (i = 0; i < ARRAY_SIZE(stream_ch_modes); i++) @@ -1486,7 +1518,6 @@ static int config_set(int id, GVariant *data, struct sr_dev_inst *sdi, __func__, devc->ch_mode); } else if (id == SR_CONF_THRESHOLD) { stropt = g_variant_get_string(data, NULL); - ret = SR_OK; if (!strcmp(stropt, thresholds[SR_TH_3V3])) { devc->th_level = SR_TH_3V3; } else if (!strcmp(stropt, thresholds[SR_TH_5V0])) { @@ -1527,16 +1558,10 @@ static int config_set(int id, GVariant *data, struct sr_dev_inst *sdi, __func__, devc->th_level); } else if (id == SR_CONF_VTH) { devc->vth = g_variant_get_double(data); - ret = command_wr_reg(usb->devhdl, (uint8_t)(devc->vth/5.0*255), VTH_ADDR); - if (ret == SR_OK) - sr_dbg("%s: setting threshold voltage to %d", - __func__, devc->vth); - else - sr_dbg("%s: setting threshold voltage to %d failed", - __func__, devc->vth); + sr_dbg("%s: setting threshold voltage to %f failed", + __func__, devc->vth); } else if (id == SR_CONF_FILTER) { stropt = g_variant_get_string(data, NULL); - ret = SR_OK; if (!strcmp(stropt, filters[SR_FILTER_NONE])) { devc->filter = SR_FILTER_NONE; } else if (!strcmp(stropt, filters[SR_FILTER_1T])) { @@ -1544,11 +1569,10 @@ static int config_set(int id, GVariant *data, struct sr_dev_inst *sdi, } else { ret = SR_ERR; } - sr_dbg("%s: setting threshold to %d", - __func__, devc->th_level); + sr_dbg("%s: setting filter to %d", + __func__, devc->filter); } else if (id == SR_CONF_MAX_HEIGHT) { stropt = g_variant_get_string(data, NULL); - ret = SR_OK; for (i = 0; i < ARRAY_SIZE(maxHeights); i++) { if (!strcmp(stropt, maxHeights[i])) { devc->max_height = i; @@ -1577,8 +1601,9 @@ static int config_set(int id, GVariant *data, struct sr_dev_inst *sdi, sr_dbg("%s: setting ENABLE of channel %d to %d", __func__, ch->index, ch->enabled); } else if (id == SR_CONF_DATALOCK) { + while(libusb_try_lock_events(drvc->sr_ctx->libusb_ctx)); devc->data_lock = g_variant_get_boolean(data); - ret = SR_OK; + libusb_unlock_events(drvc->sr_ctx->libusb_ctx); } else if (id == SR_CONF_VDIV) { ch->vdiv = g_variant_get_uint64(data); if (sdi->mode == DSO) { @@ -1594,15 +1619,12 @@ static int config_set(int id, GVariant *data, struct sr_dev_inst *sdi, ch->vfactor = g_variant_get_uint64(data); sr_dbg("%s: setting Factor of channel %d to %d", __func__, ch->index, ch->vfactor); - ret = SR_OK; } else if (id == SR_CONF_VPOS) { ch->vpos = g_variant_get_double(data); sr_dbg("%s: setting VPOS of channel %d to %lf", __func__, ch->index, ch->vpos); - ret = SR_OK; } else if (id == SR_CONF_TIMEBASE) { devc->timebase = g_variant_get_uint64(data); - ret = SR_OK; } else if (id == SR_CONF_COUPLING) { ch->coupling = g_variant_get_byte(data); if (ch->coupling == SR_GND_COUPLING) @@ -1651,26 +1673,22 @@ static int config_set(int id, GVariant *data, struct sr_dev_inst *sdi, __func__, ch->index, ch->trig_value); } else if (id == SR_CONF_HORIZ_TRIGGERPOS) { if (sdi->mode == DSO) { - uint16_t channel_cnt = 0; - GSList *l; - for (l = sdi->channels; l; l = l->next) { - struct sr_channel *probe = (struct sr_channel *)l->data; - channel_cnt += probe->enabled; - } devc->trigger_hrate = g_variant_get_byte(data); - devc->trigger_hpos = devc->trigger_hrate * channel_cnt * devc->limit_samples / 200.0; + //devc->trigger_hpos = devc->trigger_hrate * en_ch_num(sdi) * devc->limit_samples / 200.0; + /* + * devc->trigger_hpos should be updated before each acquisition + * because the samplelimits may changed + */ + devc->trigger_hpos = devc->trigger_hrate * en_ch_num(sdi) * devc->limit_samples / 200.0; + if ((ret = command_dso_ctrl(usb->devhdl, dso_cmd_gen(sdi, 1, SR_CONF_HORIZ_TRIGGERPOS))) == SR_OK) + sr_dbg("%s: setting DSO Horiz Trigger Position to %d", + __func__, devc->trigger_hpos); + else + sr_dbg("%s: setting DSO Horiz Trigger Position to %d failed", + __func__, devc->trigger_hpos); } else { devc->trigger_hpos = g_variant_get_byte(data) * devc->limit_samples / 100.0; } - if (sdi->mode == DSO) { - ret = command_dso_ctrl(usb->devhdl, dso_cmd_gen(sdi, 1, SR_CONF_HORIZ_TRIGGERPOS)); - } - if (ret == SR_OK) - sr_dbg("%s: setting DSO Horiz Trigger Position to %d", - __func__, devc->trigger_hpos); - else - sr_dbg("%s: setting DSO Horiz Trigger Position to %d failed", - __func__, devc->trigger_hpos); } else if (id == SR_CONF_TRIGGER_HOLDOFF) { devc->trigger_holdoff = g_variant_get_uint64(data); if (sdi->mode == DSO) { @@ -1693,22 +1711,6 @@ static int config_set(int id, GVariant *data, struct sr_dev_inst *sdi, else sr_dbg("%s: setting Trigger Margin to %d failed", __func__, devc->trigger_margin); - } else if (id == SR_CONF_ZERO) { - devc->zero = g_variant_get_boolean(data); - ret = SR_OK; - } else if (id == SR_CONF_ZERO_SET) { - GSList *l; - for(l = sdi->channels; l; l = l->next) { - struct sr_channel *probe = (struct sr_channel *)l->data; - zero_info.zero_addr = zero_base_addr + probe->index * sizeof(struct cmd_zero_info); - ret = command_wr_nvm(usb->devhdl, (unsigned char *)&zero_info, sizeof(struct cmd_zero_info)); - if (ret != SR_OK) - sr_err("DSO channel %d Set Zero command failed!", probe->index); - } - if (ret != SR_OK) - sr_err("DSO Set Comb command failed!"); - else - devc->zero = FALSE; } else if (id == SR_CONF_STREAM) { devc->stream = g_variant_get_boolean(data); } else { @@ -1772,7 +1774,7 @@ static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi, case SR_CONF_LIMIT_SAMPLES: g_variant_builder_init(&gvb, G_VARIANT_TYPE("a{sv}")); gvar = g_variant_new_from_data(G_VARIANT_TYPE("at"), - samplecounts, ARRAY_SIZE(samplecounts)*sizeof(uint64_t), TRUE, NULL, NULL); + samplecounts, devc->samplecounts_size*sizeof(uint64_t), TRUE, NULL, NULL); g_variant_builder_add(&gvb, "{sv}", "samplecounts", gvar); *data = g_variant_builder_end(&gvb); break; @@ -1813,41 +1815,25 @@ static int dev_open(struct sr_dev_inst *sdi) GSList *l; int ret; int64_t timediff_us, timediff_ms; + uint8_t fpga_done; devc = sdi->priv; usb = sdi->conn; /* - * If the firmware was recently uploaded, wait up to MAX_RENUM_DELAY_MS - * milliseconds for the FX2 to renumerate. + * If the firmware was recently uploaded, no dev_open operation should be called. + * Just wait for renumerate -> detach -> attach */ ret = SR_ERR; - if (devc->fw_updated > 0) { - sr_info("Waiting for device to reset."); - /* Takes >= 300ms for the FX2 to be gone from the USB bus. */ - g_usleep(300 * 1000); - timediff_ms = 0; - while (timediff_ms < MAX_RENUM_DELAY_MS) { - if ((ret = DSLogic_dev_open(sdi)) == SR_OK) - break; - g_usleep(100 * 1000); - - timediff_us = g_get_monotonic_time() - devc->fw_updated; - timediff_ms = timediff_us / 1000; - sr_spew("Waited %" PRIi64 "ms.", timediff_ms); - } - if (ret != SR_OK) { - sr_err("Device failed to renumerate."); - return SR_ERR; - } - sr_info("Device came back after %" PRIi64 "ms.", timediff_ms); + if (devc->fw_updated > 0) { + return SR_ERR; } else { - sr_info("Firmware upload was not needed."); + sr_info("%s: Firmware upload was not needed.", __func__); ret = DSLogic_dev_open(sdi); } if (ret != SR_OK) { - sr_err("Unable to open device."); + sr_err("%s: Unable to open device.", __func__); return SR_ERR; } @@ -1855,24 +1841,30 @@ static int dev_open(struct sr_dev_inst *sdi) if (ret != 0) { switch(ret) { case LIBUSB_ERROR_BUSY: - sr_err("Unable to claim USB interface. Another " - "program or driver has already claimed it."); + sr_err("%s: Unable to claim USB interface. Another " + "program or driver has already claimed it.", __func__); break; case LIBUSB_ERROR_NO_DEVICE: - sr_err("Device has been disconnected."); + sr_err("%s: Device has been disconnected.", __func__); break; default: - sr_err("Unable to claim interface: %s.", - libusb_error_name(ret)); + sr_err("%s: Unable to claim interface: %s.", + __func__, libusb_error_name(ret)); break; } return SR_ERR; } - if (devc->fw_updated > 0) { + ret = command_get_fpga_done(usb->devhdl, &fpga_done); + if (ret != SR_OK) { + sr_err("Failed to get fpga done infos."); + return SR_ERR; + } + + if (fpga_done == 0) { if ((ret = command_fpga_config(usb->devhdl)) != SR_OK) { - sr_err("Send FPGA configure command failed!"); + sr_err("%s: Send FPGA configure command failed!", __func__); } else { /* Takes >= 10ms for the FX2 to be ready for FPGA configure. */ g_usleep(10 * 1000); @@ -1894,34 +1886,12 @@ static int dev_open(struct sr_dev_inst *sdi) } ret = fpga_config(usb->devhdl, fpga_bit); if (ret != SR_OK) { - sr_err("Configure FPGA failed!"); + sr_err("%s: Configure FPGA failed!", __func__); } g_free(fpga_bit); } } - ret = command_wr_reg(usb->devhdl, (uint8_t)(devc->vth/5.0*255), VTH_ADDR); - if (ret == SR_OK) - sr_dbg("%s: setting threshold voltage to %d", - __func__, devc->vth); - else - sr_dbg("%s: setting threshold voltage to %d failed", - __func__, devc->vth); - - #ifdef _WIN32 - if (pipe(devc->pipe_fds)) { - /* TODO: Better error message. */ - sr_err("%s: pipe() failed", __func__); - return SR_ERR; - } - devc->channel = g_io_channel_unix_new(devc->pipe_fds[0]); - g_io_channel_set_flags(devc->channel, G_IO_FLAG_NONBLOCK, NULL); - /* Set channel encoding to binary (default is UTF-8). */ - g_io_channel_set_encoding(devc->channel, NULL, NULL); - /* Make channels to unbuffered. */ - g_io_channel_set_buffered(devc->channel, FALSE); - #endif - return SR_OK; } @@ -1935,14 +1905,6 @@ static int dev_close(struct sr_dev_inst *sdi) return SR_ERR; devc = sdi->priv; - #ifdef _WIN32 - if (sdi->status == SR_ST_ACTIVE && devc->channel) { - g_io_channel_shutdown(devc->channel, FALSE, NULL); - g_io_channel_unref(devc->channel); - devc->channel = NULL; - } - #endif - sr_info("DSLogic: Closing device %d on %d.%d interface %d.", sdi->index, usb->bus, usb->address, USB_INTERFACE); libusb_release_interface(usb->devhdl, USB_INTERFACE); @@ -1969,52 +1931,31 @@ static int cleanup(void) return ret; } -static void abort_acquisition(struct DSL_context *devc) +static void remove_sources(struct DSL_context *devc) { int i; - int ret; - struct sr_usb_dev_inst *usb; - - devc->num_samples = -1; - - sr_info("%s: Stopping", __func__); - - /* Stop GPIF acquisition */ - usb = ((struct sr_dev_inst *)devc->cb_data)->conn; - if ((ret = command_stop_acquisition (usb->devhdl)) != SR_OK) - sr_err("Stop DSLogic acquisition failed!"); - else - sr_info("Stop DSLogic acquisition!"); - - /* Cancel exist transfers */ - if (devc->num_transfers) - for (i = devc->num_transfers - 1; i >= 0; i--) { - if (devc->transfers[i]) - libusb_cancel_transfer(devc->transfers[i]); - } + sr_err("%s: remove fds from polling", __func__); + /* Remove fds from polling. */ + for (i = 0; devc->usbfd[i] != -1; i++) + sr_source_remove(devc->usbfd[i]); + g_free(devc->usbfd); } static void finish_acquisition(struct DSL_context *devc) { struct sr_datafeed_packet packet; - int i, ret; - struct sr_usb_dev_inst *usb; - sr_err("finish acquisition: send SR_DF_END packet"); + sr_err("%s: send SR_DF_END packet", __func__); /* Terminate session. */ packet.type = SR_DF_END; sr_session_send(devc->cb_data, &packet); - - sr_err("finish acquisition: remove fds from polling"); - /* Remove fds from polling. */ - for (i = 0; devc->usbfd[i] != -1; i++) - sr_source_remove(devc->usbfd[i]); - g_free(devc->usbfd); if (devc->num_transfers != 0) { devc->num_transfers = 0; g_free(devc->transfers); } + + devc->status = DSL_FINISH; } static void free_transfer(struct libusb_transfer *transfer) @@ -2022,7 +1963,7 @@ static void free_transfer(struct libusb_transfer *transfer) struct DSL_context *devc; unsigned int i; - devc = transfer->user_data; + devc = transfer->user_data; g_free(transfer->buffer); transfer->buffer = NULL; @@ -2061,42 +2002,32 @@ static void receive_transfer(struct libusb_transfer *transfer) struct sr_datafeed_logic logic; struct sr_datafeed_dso dso; struct sr_datafeed_analog analog; - struct DSL_context *devc; - int trigger_offset, i, sample_width, cur_sample_count; - int trigger_offset_bytes; - uint8_t *cur_buf; - //GTimeVal cur_time; - //g_get_current_time(&cur_time); - //sr_info("receive_transfer: current time %d sec %d usec", cur_time.tv_sec, cur_time.tv_usec); + int trigger_offset, i; + int trigger_offset_bytes; - - devc = transfer->user_data; - - /* - * If acquisition has already ended, just free any queued up - * transfer that come in. - */ - if (devc->num_samples == -1) { - free_transfer(transfer); + const uint8_t *cur_buf = transfer->buffer; + struct DSL_context *devc = transfer->user_data; + struct sr_dev_inst *sdi = devc->cb_data; + const int sample_width = (devc->sample_wide) ? 2 : 1; + int cur_sample_count = transfer->actual_length / sample_width; + + if (devc->data_lock) { + resubmit_transfer(transfer); return; } - //sr_info("receive_transfer(): status %d; timeout %d; received %d bytes.", - // transfer->status, transfer->timeout, transfer->actual_length); + if (devc->abort) + devc->status = DSL_STOP; - /* Save incoming transfer before reusing the transfer struct. */ - cur_buf = transfer->buffer; - - sample_width = (devc->sample_wide) ? 2 : 1; - cur_sample_count = transfer->actual_length / sample_width; + sr_info("receive_transfer(): status %d; timeout %d; received %d bytes.", + transfer->status, transfer->timeout, transfer->actual_length); switch (transfer->status) { case LIBUSB_TRANSFER_NO_DEVICE: - //abort_acquisition(devc); - free_transfer(transfer); + case LIBUSB_TRANSFER_CANCELLED: devc->status = DSL_ERROR; - return; + break; case LIBUSB_TRANSFER_COMPLETED: case LIBUSB_TRANSFER_TIMED_OUT: /* We may have received some data though. */ break; @@ -2105,30 +2036,20 @@ static void receive_transfer(struct libusb_transfer *transfer) break; } - if (transfer->actual_length == 0 || - packet_has_error || - devc->data_lock) { + if (devc->status == DSL_DATA && + (transfer->actual_length == 0 || + packet_has_error)) { devc->empty_transfer_count++; if (devc->empty_transfer_count > MAX_EMPTY_TRANSFERS) { - /* - * The FX2 gave up. End the acquisition, the frontend - * will work out that the samplecount is short. - */ - //abort_acquisition(devc); - free_transfer(transfer); devc->status = DSL_ERROR; - } else { - resubmit_transfer(transfer); } - return; } else { devc->empty_transfer_count = 0; } trigger_offset = 0; - if (devc->trigger_stage >= 0) { + if (devc->status == DSL_DATA && devc->trigger_stage >= 0) { for (i = 0; i < cur_sample_count; i++) { - const uint16_t cur_sample = devc->sample_wide ? *((const uint16_t*)cur_buf + i) : *((const uint8_t*)cur_buf + i); @@ -2150,7 +2071,7 @@ static void receive_transfer(struct libusb_transfer *transfer) */ packet.type = SR_DF_TRIGGER; packet.payload = NULL; - sr_session_send(devc->cb_data, &packet); + sr_session_send(sdi, &packet); /* * Send the samples that triggered it, @@ -2161,7 +2082,7 @@ static void receive_transfer(struct libusb_transfer *transfer) logic.unitsize = sizeof(*devc->trigger_buffer); logic.length = devc->trigger_stage * logic.unitsize; logic.data = devc->trigger_buffer; - sr_session_send(devc->cb_data, &packet); + sr_session_send(sdi, &packet); devc->trigger_stage = TRIGGER_FIRED; break; @@ -2183,73 +2104,70 @@ static void receive_transfer(struct libusb_transfer *transfer) } } - if (devc->trigger_stage == TRIGGER_FIRED) { + if (devc->status == DSL_DATA && devc->trigger_stage == TRIGGER_FIRED) { /* Send the incoming transfer to the session bus. */ trigger_offset_bytes = trigger_offset * sample_width; - if ((*(struct sr_dev_inst *)(devc->cb_data)).mode == LOGIC) { + // check packet type + if (sdi->mode == LOGIC) { packet.type = SR_DF_LOGIC; packet.payload = &logic; logic.length = transfer->actual_length - trigger_offset_bytes; logic.unitsize = sample_width; logic.data_error = 0; logic.data = cur_buf + trigger_offset_bytes; - } else if ((*(struct sr_dev_inst *)(devc->cb_data)).mode == DSO) { - uint16_t channel_cnt = 0; - uint16_t channel_en_cnt = 0; - GSList *l; - struct sr_dev_inst *sdi = devc->cb_data; - for (l = sdi->channels; l; l = l->next) { - struct sr_channel *probe = (struct sr_channel *)l->data; - channel_cnt ++; - channel_en_cnt += probe->enabled; - } - if (channel_en_cnt == 0) - channel_en_cnt = 1; - + } else if (sdi->mode == DSO) { if (!devc->instant) { - const uint32_t mstatus_offset = devc->limit_samples / (channel_cnt/channel_en_cnt); + const uint32_t mstatus_offset = devc->limit_samples / (g_slist_length(sdi->channels)/en_ch_num(sdi)); + mstatus.pkt_id = *((const uint16_t*)cur_buf + mstatus_offset); mstatus.ch0_max = *((const uint8_t*)cur_buf + mstatus_offset*2 + 1*2); mstatus.ch0_min = *((const uint8_t*)cur_buf + mstatus_offset*2 + 3); mstatus.ch0_period = *((const uint32_t*)cur_buf + mstatus_offset/2 + 2/2); - mstatus.ch0_period += *((const uint32_t*)cur_buf + mstatus_offset/2 + 4/2) << 32; + mstatus.ch0_period += ((uint64_t)*((const uint32_t*)cur_buf + mstatus_offset/2 + 4/2)) << 32; mstatus.ch0_pcnt = *((const uint32_t*)cur_buf + mstatus_offset/2 + 6/2); mstatus.ch1_max = *((const uint8_t*)cur_buf + mstatus_offset*2 + 9*2); mstatus.ch1_min = *((const uint8_t*)cur_buf + mstatus_offset*2 + 19); mstatus.ch1_period = *((const uint32_t*)cur_buf + mstatus_offset/2 + 10/2); - mstatus.ch1_period += *((const uint32_t*)cur_buf + mstatus_offset/2 + 12/2) << 32; + mstatus.ch1_period += ((uint64_t)*((const uint32_t*)cur_buf + mstatus_offset/2 + 12/2)) << 32; mstatus.ch1_pcnt = *((const uint32_t*)cur_buf + mstatus_offset/2 + 14/2); mstatus.vlen = *((const uint32_t*)cur_buf + mstatus_offset/2 + 16/2) & 0x7fffffff; mstatus.stream_mode = *((const uint32_t*)cur_buf + mstatus_offset/2 + 16/2) & 0x80000000; - mstatus.sample_divider = *((const uint32_t*)cur_buf + mstatus_offset/2 + 18/2) & 0x7fffffff; + mstatus.sample_divider = *((const uint32_t*)cur_buf + mstatus_offset/2 + 18/2) & 0x0fffffff; mstatus.sample_divider_tog = *((const uint32_t*)cur_buf + mstatus_offset/2 + 18/2) & 0x80000000; + mstatus.trig_flag = *((const uint32_t*)cur_buf + mstatus_offset/2 + 18/2) & 0x40000000; } else { mstatus.vlen = instant_buffer_size; } - const uint32_t divider = devc->zero ? 0x1 : (uint32_t)ceil(DSLOGIC_MAX_DSO_SAMPLERATE * 1.0 / devc->cur_samplerate / channel_en_cnt); - if ((mstatus.sample_divider == divider && - mstatus.vlen != 0 && - mstatus.vlen <= (transfer->actual_length - 512) / sample_width) || + + const uint32_t divider = devc->zero ? 0x1 : (uint32_t)ceil(DSCOPE_MAX_SAMPLERATE * 1.0 / devc->cur_samplerate / en_ch_num(sdi)); + if ((mstatus.pkt_id == DSO_PKTID && + mstatus.sample_divider == divider && + mstatus.vlen != 0 && + mstatus.vlen <= (transfer->actual_length - 512) / sample_width) || devc->instant) { - devc->mstatus_valid = devc->instant ? FALSE : TRUE; + devc->stream = (mstatus.stream_mode != 0); + devc->mstatus_valid = TRUE; packet.type = SR_DF_DSO; packet.payload = &dso; - dso.probes = (*(struct sr_dev_inst *)(devc->cb_data)).channels; + dso.probes = sdi->channels; //dso.num_samples = (transfer->actual_length - 512) / sample_width; - cur_sample_count = 2 * mstatus.vlen / channel_en_cnt ; + cur_sample_count = 2 * mstatus.vlen / en_ch_num(sdi) ; dso.num_samples = cur_sample_count; dso.mq = SR_MQ_VOLTAGE; dso.unit = SR_UNIT_VOLT; dso.mqflags = SR_MQFLAG_AC; - dso.samplerate_tog = mstatus.sample_divider_tog; + dso.samplerate_tog = (mstatus.sample_divider_tog != 0); + dso.trig_flag = (mstatus.trig_flag != 0); dso.data = cur_buf + trigger_offset_bytes; } else { + packet.type = SR_DF_ABANDON; devc->mstatus_valid = FALSE; } } else { packet.type = SR_DF_ANALOG; packet.payload = &analog; - analog.probes = (*(struct sr_dev_inst *)(devc->cb_data)).channels; - analog.num_samples = (transfer->actual_length / sample_width)/g_slist_length(analog.probes); + analog.probes = sdi->channels; + cur_sample_count = transfer->actual_length / (sample_width * g_slist_length(analog.probes)); + analog.num_samples = cur_sample_count; analog.mq = SR_MQ_VOLTAGE; analog.unit = SR_UNIT_VOLT; analog.mqflags = SR_MQFLAG_AC; @@ -2299,27 +2217,22 @@ static void receive_transfer(struct libusb_transfer *transfer) } /* send data to session bus */ - sr_session_send(devc->cb_data, &packet); - devc->sent_samples += cur_sample_count; + if (packet.type != SR_DF_ABANDON) + sr_session_send(sdi, &packet); } devc->num_samples += cur_sample_count; - if (((*(struct sr_dev_inst *)(devc->cb_data)).mode == LOGIC || devc->instant) && - devc->limit_samples && - (unsigned int)devc->num_samples >= devc->actual_samples) { - //abort_acquisition(devc); - free_transfer(transfer); + if ((sdi->mode != DSO || devc->instant) && + devc->limit_samples && + (unsigned int)devc->num_samples >= devc->actual_samples) { devc->status = DSL_STOP; - return; } - } else { - /* - * TODO: Buffer pre-trigger data in capture - * ratio-sized buffer. - */ } - resubmit_transfer(transfer); + if (devc->status == DSL_DATA) + resubmit_transfer(transfer); + else + free_transfer(transfer); } static unsigned int to_bytes_per_ms(struct DSL_context *devc) @@ -2327,7 +2240,7 @@ static unsigned int to_bytes_per_ms(struct DSL_context *devc) if (devc->cur_samplerate > SR_MHZ(100)) return SR_MHZ(100) / 1000 * (devc->sample_wide ? 2 : 1); else - return devc->cur_samplerate / 1000 * (devc->sample_wide ? 2 : 1); + return devc->cur_samplerate / 1000 * (devc->sample_wide ? 2 : 1) ; } static size_t get_buffer_size(struct DSL_context *devc) @@ -2371,7 +2284,7 @@ static int dev_transfer_start(const struct sr_dev_inst *sdi) struct DSL_context *devc; struct sr_usb_dev_inst *usb; struct libusb_transfer *transfer; - unsigned int i, timeout, num_transfers; + unsigned int i, num_transfers; int ret; unsigned char *buf; size_t size; @@ -2380,34 +2293,26 @@ static int dev_transfer_start(const struct sr_dev_inst *sdi) devc = sdi->priv; usb = sdi->conn; - uint16_t channel_en_cnt = 0; - uint16_t channel_cnt = 0; - GSList *l; - for (l = sdi->channels; l; l = l->next) { - struct sr_channel *probe = (struct sr_channel *)l->data; - channel_en_cnt += probe->enabled; - channel_cnt++; - } if (devc->instant) - dso_buffer_size = instant_buffer_size * channel_cnt; + dso_buffer_size = instant_buffer_size * g_slist_length(sdi->channels); else - dso_buffer_size = devc->limit_samples * channel_en_cnt + 512; + dso_buffer_size = devc->limit_samples * en_ch_num(sdi) + 512; num_transfers = 1; - size = (sdi->mode == ANALOG) ? cons_buffer_size : ((sdi->mode == DSO) ? dso_buffer_size : get_buffer_size(devc)); + size = (sdi->mode == DSO) ? dso_buffer_size : + (devc->op_mode == SR_OP_STREAM) ? get_buffer_size(devc) : instant_buffer_size; devc->submitted_transfers = 0; devc->transfers = g_try_malloc0(sizeof(*devc->transfers) * num_transfers); if (!devc->transfers) { - sr_err("USB transfers malloc failed."); + sr_err("%s: USB transfers malloc failed.", __func__); return SR_ERR_MALLOC; } - devc->num_transfers = num_transfers; for (i = 0; i < num_transfers; i++) { if (!(buf = g_try_malloc(size))) { - sr_err("USB transfer buffer malloc failed."); + sr_err("%s: USB transfer buffer malloc failed.", __func__); return SR_ERR_MALLOC; } transfer = libusb_alloc_transfer(0); @@ -2415,15 +2320,17 @@ static int dev_transfer_start(const struct sr_dev_inst *sdi) 6 | LIBUSB_ENDPOINT_IN, buf, size, receive_transfer, devc, 0); if ((ret = libusb_submit_transfer(transfer)) != 0) { - sr_err("Failed to submit transfer: %s.", - libusb_error_name(ret)); + sr_err("%s: Failed to submit transfer: %s.", + __func__, libusb_error_name(ret)); libusb_free_transfer(transfer); g_free(buf); - abort_acquisition(devc); + devc->status = DSL_ERROR; + devc->abort = TRUE; return SR_ERR; } devc->transfers[i] = transfer; devc->submitted_transfers++; + devc->num_transfers++; } devc->status = DSL_DATA; @@ -2431,30 +2338,43 @@ static int dev_transfer_start(const struct sr_dev_inst *sdi) return SR_OK; } + static int receive_data(int fd, int revents, const struct sr_dev_inst *sdi) { int completed = 0; struct timeval tv; struct drv_context *drvc; struct DSL_context *devc; + struct sr_usb_dev_inst *usb; + int i; + int ret; (void)fd; (void)revents; drvc = di->priv; devc = sdi->priv; - - if (devc->num_samples != -1 && - (devc->status == DSL_STOP || devc->status == DSL_ERROR)) { - if (devc->sent_samples < devc->actual_samples) - devc->sent_samples = 0; - sr_info("%s: Stopping", __func__); - abort_acquisition(devc); - } + usb = sdi->conn; tv.tv_sec = tv.tv_usec = 0; libusb_handle_events_timeout_completed(drvc->sr_ctx->libusb_ctx, &tv, &completed); + if (devc->status == DSL_FINISH) { + if (libusb_try_lock_events(drvc->sr_ctx->libusb_ctx) == 0) { + if (libusb_event_handling_ok(drvc->sr_ctx->libusb_ctx)) { + /* Stop GPIF acquisition */ + usb = ((struct sr_dev_inst *)devc->cb_data)->conn; + if ((ret = command_stop_acquisition (usb->devhdl)) != SR_OK) + sr_err("%s: Sent acquisition stop command failed!", __func__); + else + sr_info("%s: Sent acquisition stop command!", __func__); + + remove_sources(devc); + } + libusb_unlock_events(drvc->sr_ctx->libusb_ctx); + } + } + return TRUE; } @@ -2462,78 +2382,43 @@ static void receive_trigger_pos(struct libusb_transfer *transfer) { struct DSL_context *devc; struct sr_datafeed_packet packet; - struct sr_datafeed_logic logic; - struct sr_datafeed_dso dso; - struct sr_datafeed_analog analog; struct ds_trigger_pos *trigger_pos; const struct sr_dev_inst *sdi; int ret; devc = transfer->user_data; - sdi = devc->cb_data; - - sr_info("receive_trigger_pos(): status %d; timeout %d; received %d bytes.", - transfer->status, transfer->timeout, transfer->actual_length); - - if (devc->num_samples == -1) { - free_transfer(transfer); - return; - } - + sdi = devc->cb_data; trigger_pos = (struct ds_trigger_pos *)transfer->buffer; - switch (transfer->status) { - case LIBUSB_TRANSFER_COMPLETED: + devc->status = DSL_ERROR; + if (transfer->status == LIBUSB_TRANSFER_COMPLETED && + trigger_pos->check_id == TRIG_CHECKID) { + sr_info("receive_trigger_pos(): status %d; timeout %d; received %d bytes.", + transfer->status, transfer->timeout, transfer->actual_length); if (transfer->actual_length == sizeof(struct ds_trigger_pos)) { if (sdi->mode != LOGIC || devc->stream || trigger_pos->remain_cnt < devc->limit_samples) { if (sdi->mode == LOGIC && !devc->stream) devc->actual_samples = (devc->limit_samples - ceil(devc->cur_samplerate * 1.0 / DSLOGIC_MAX_LOGIC_SAMPLERATE) * (trigger_pos->remain_cnt)); - packet.type = SR_DF_TRIGGER; packet.payload = trigger_pos; sr_session_send(sdi, &packet); devc->status = DSL_TRIGGERED; - free_transfer(transfer); devc->num_transfers = 0; devc->empty_transfer_count = 0; - } else { - free_transfer(transfer); - devc->status = DSL_ERROR; } - } else { - free_transfer(transfer); - devc->status = DSL_ERROR; } - break; - case LIBUSB_TRANSFER_TIMED_OUT: - devc->empty_transfer_count++; - if (devc->empty_transfer_count > MAX_EMPTY_TRANSFERS) { - /* - * The FX2 gave up. End the acquisition, the frontend - * will work out that the samplecount is short. - */ - //abort_acquisition(devc); - free_transfer(transfer); - devc->status = DSL_ERROR; - } else { - resubmit_transfer(transfer); - } - break; - case LIBUSB_TRANSFER_CANCELLED: - resubmit_transfer(transfer); - break; - default: - //abort_acquisition(devc); - free_transfer(transfer); - devc->status = DSL_ERROR; - break; } if (devc->status == DSL_TRIGGERED) { - if ((ret = dev_transfer_start(sdi)) != SR_OK) { + // successfull + free_transfer(transfer); + if ((ret = dev_transfer_start(devc->cb_data)) != SR_OK) { sr_err("%s: could not start data transfer" "(%d)%d", __func__, ret, errno); } + } else { + // failed + free_transfer(transfer); } } @@ -2563,63 +2448,84 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data) //devc->cb_data = cb_data; devc->cb_data = sdi; devc->num_samples = 0; - devc->sent_samples = 0; devc->empty_transfer_count = 0; devc->status = DSL_INIT; devc->num_transfers = 0; devc->submitted_transfers = 0; devc->actual_samples = devc->limit_samples; + test_sample_value = 0; + devc->abort = FALSE; /* Configures devc->trigger_* and devc->sample_wide */ if (configure_probes(sdi) != SR_OK) { - sr_err("Failed to configure probes."); + sr_err("%s: Failed to configure probes.", __func__); return SR_ERR; } /* Stop Previous GPIF acquisition */ if ((ret = command_stop_acquisition (usb->devhdl)) != SR_OK) { - sr_err("Stop DSLogic acquisition failed!"); - abort_acquisition(devc); + sr_err("%s: Stop DSLogic acquisition failed!", __func__); return ret; } else { - sr_info("Stop Previous DSLogic acquisition!"); + sr_info("%s: Stop Previous DSLogic acquisition!", __func__); } /* Setting FPGA before acquisition start*/ if ((ret = command_fpga_setting(usb->devhdl, sizeof(struct DSL_setting) / sizeof(uint16_t))) != SR_OK) { - sr_err("Send FPGA setting command failed!"); + sr_err("%s: Send FPGA setting command failed!", __func__); } else { if ((ret = fpga_setting(sdi)) != SR_OK) { - sr_err("Configure FPGA failed!"); - abort_acquisition(devc); + sr_err("%s: Configure FPGA failed!", __func__); return ret; } } + /* + * settings must be updated before acquisition + */ if (sdi->mode == DSO) { - GList *l; - for(l = sdi->channels; l; l = l->next) { - struct sr_channel *probe = (struct sr_channel *)l->data; - ret = command_dso_ctrl(usb->devhdl, dso_cmd_gen(sdi, probe, SR_CONF_COUPLING)); - if (ret != SR_OK) { - sr_err("Set Coupling command failed!"); - return ret; - } - ret = command_dso_ctrl(usb->devhdl, dso_cmd_gen(sdi, probe, SR_CONF_VDIV)); - if (ret != SR_OK) { - sr_dbg("%s: Initial setting for DSO mode failed", __func__); - return ret; - } + devc->trigger_hpos = devc->trigger_hrate * en_ch_num(sdi) * devc->limit_samples / 200.0; + if ((ret = command_dso_ctrl(usb->devhdl, dso_cmd_gen(sdi, 1, SR_CONF_HORIZ_TRIGGERPOS))) == SR_OK) + sr_dbg("%s: setting DSO Horiz Trigger Position to %d", + __func__, devc->trigger_hpos); + else + sr_dbg("%s: setting DSO Horiz Trigger Position to %d failed", + __func__, devc->trigger_hpos); + } else if (sdi->mode == LOGIC) { + if ((ret = command_wr_reg(usb->devhdl, (uint8_t)(devc->vth/5.0*255), VTH_ADDR)) == SR_OK) { + sr_err("%s: setting threshold voltage to %f", + __func__, devc->vth); + } else { + sr_info("%s: setting threshold voltage to %f failed", + __func__, devc->vth); } } - if ((ret = command_start_acquisition (usb->devhdl, - devc->cur_samplerate, 1, (sdi->mode == LOGIC))) != SR_OK) { - abort_acquisition(devc); - return ret; + /* poll trigger status transfer*/ + if (!(trigger_pos = g_try_malloc0(sizeof(struct ds_trigger_pos)))) { + sr_err("%s: USB trigger_pos buffer malloc failed.", __func__); + return SR_ERR_MALLOC; + } + devc->transfers = g_try_malloc0(sizeof(*devc->transfers)); + if (!devc->transfers) { + sr_err("%s: USB trigger_pos transfer malloc failed.", __func__); + return SR_ERR_MALLOC; + } + transfer = libusb_alloc_transfer(0); + libusb_fill_bulk_transfer(transfer, usb->devhdl, + 6 | LIBUSB_ENDPOINT_IN, trigger_pos, sizeof(struct ds_trigger_pos), + receive_trigger_pos, devc, 0); + if ((ret = libusb_submit_transfer(transfer)) != 0) { + sr_err("%s: Failed to submit trigger_pos transfer: %s.", + __func__, libusb_error_name(ret)); + libusb_free_transfer(transfer); + g_free(trigger_pos); + return SR_ERR; + } else { + devc->num_transfers++; + devc->transfers[0] = transfer; + devc->submitted_transfers++; } - - test_sample_value = 0; /* setup callback function for data transfer */ lupfd = libusb_get_pollfds(drvc->sr_ctx->libusb_ctx); @@ -2628,40 +2534,21 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data) return SR_ERR; for (i = 0; lupfd[i]; i++) { sr_source_add(lupfd[i]->fd, lupfd[i]->events, - get_timeout(devc), receive_data, sdi); - devc->usbfd[i] = lupfd[i]->fd; + get_timeout(devc), receive_data, sdi); + devc->usbfd[i] = lupfd[i]->fd; } devc->usbfd[i] = -1; free(lupfd); - - /* poll trigger status transfer*/ - if (!(trigger_pos = g_try_malloc0(sizeof(struct ds_trigger_pos)))) { - sr_err("USB trigger_pos buffer malloc failed."); - return SR_ERR_MALLOC; - } - devc->transfers = g_try_malloc0(sizeof(*devc->transfers)); - if (!devc->transfers) { - sr_err("USB trigger_pos transfer malloc failed."); - return SR_ERR_MALLOC; - } - devc->num_transfers = 1; - transfer = libusb_alloc_transfer(0); - libusb_fill_bulk_transfer(transfer, usb->devhdl, - 6 | LIBUSB_ENDPOINT_IN, trigger_pos, sizeof(struct ds_trigger_pos), - receive_trigger_pos, devc, 0); - if ((ret = libusb_submit_transfer(transfer)) != 0) { - sr_err("Failed to submit trigger_pos transfer: %s.", - libusb_error_name(ret)); - libusb_free_transfer(transfer); - g_free(trigger_pos); - abort_acquisition(devc); - return SR_ERR; - } - devc->transfers[0] = transfer; - devc->submitted_transfers++; devc->status = DSL_START; devc->mstatus_valid = FALSE; + if ((ret = command_start_acquisition (usb->devhdl, + devc->cur_samplerate, devc->sample_wide, (sdi->mode == LOGIC))) != SR_OK) { + devc->status = DSL_ERROR; + devc->abort = TRUE; + return ret; + } + /* Send header packet to the session bus. */ //std_session_send_df_header(cb_data, LOG_PREFIX); std_session_send_df_header(sdi, LOG_PREFIX); @@ -2674,12 +2561,15 @@ static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data) (void)cb_data; struct DSL_context *devc; + struct sr_usb_dev_inst *usb; devc = sdi->priv; - devc->status = DSL_STOP; + usb = sdi->conn; - sr_info("%s: Stopping", __func__); - //abort_acquisition(sdi->priv); + if (!devc->abort) { + devc->abort = TRUE; + command_wr_reg(usb->devhdl, 3, EEWP_ADDR); + } return SR_OK; } @@ -2704,30 +2594,22 @@ static int dev_test(struct sr_dev_inst *sdi) static int dev_status_get(struct sr_dev_inst *sdi, struct sr_status *status, int begin, int end) { + int ret = SR_ERR; if (sdi) { - struct sr_usb_dev_inst *usb; - int ret = SR_ERR; struct DSL_context *devc; + struct sr_usb_dev_inst *usb; devc = sdi->priv; usb = sdi->conn; - if (sdi->mode == DSO) { - if (devc->mstatus_valid) { - *status = mstatus; - if (devc->zero) - return SR_ERR; - else - return SR_OK; - } - } else { - if (devc->status == DSL_START) - ret = command_get_status(usb->devhdl, (unsigned char*)status, begin, end); + if (devc->status == DSL_START) { + ret = command_get_status(usb->devhdl, (unsigned char*)status, begin, end); + } else if (devc->mstatus_valid) { + *status = mstatus; + ret = SR_OK; } - - return ret; - } else { - return SR_ERR; } + + return ret; } SR_PRIV struct sr_dev_driver DSLogic_driver_info = { diff --git a/libsigrok4DSL/hardware/demo/demo.c b/libsigrok4DSL/hardware/demo/demo.c index 0cd35c31..6c279323 100644 --- a/libsigrok4DSL/hardware/demo/demo.c +++ b/libsigrok4DSL/hardware/demo/demo.c @@ -100,6 +100,7 @@ struct dev_context { GIOChannel *channel; uint64_t cur_samplerate; uint64_t limit_samples; + uint64_t limit_samples_show; uint64_t limit_msec; uint8_t sample_generator; uint64_t samples_counter; @@ -285,6 +286,18 @@ static const char *probe_names[NUM_PROBES + 1] = { }; +static const gboolean default_ms_en[DSO_MS_END - DSO_MS_BEGIN] = { + FALSE, /* DSO_MS_BEGIN */ + TRUE, /* DSO_MS_FREQ */ + FALSE, /* DSO_MS_PERD */ + TRUE, /* DSO_MS_VMAX */ + TRUE, /* DSO_MS_VMIN */ + FALSE, /* DSO_MS_VRMS */ + FALSE, /* DSO_MS_VMEA */ + FALSE, /* DSO_MS_VP2P */ + FALSE, /* DSO_MS_END */ +}; + /* Private, per-device-instance driver context. */ /* TODO: struct context as with the other drivers. */ @@ -341,6 +354,7 @@ static GSList *hw_scan(GSList *options) devc->sdi = sdi; devc->cur_samplerate = SR_MHZ(1); devc->limit_samples = SR_MB(1); + devc->limit_samples_show = devc->limit_samples; devc->limit_msec = 0; devc->sample_generator = PATTERN_SINE; devc->timebase = 200; @@ -457,6 +471,20 @@ static int hw_cleanup(void) return ret; } +static int en_ch_num(const struct sr_dev_inst *sdi) +{ + GSList *l; + int channel_en_cnt = 0; + + for (l = sdi->channels; l; l = l->next) { + struct sr_channel *probe = (struct sr_channel *)l->data; + channel_en_cnt += probe->enabled; + } + channel_en_cnt += (channel_en_cnt == 0); + + return channel_en_cnt; +} + static int config_get(int id, GVariant **data, const struct sr_dev_inst *sdi, const struct sr_channel *ch, const struct sr_channel_group *cg) @@ -470,7 +498,7 @@ static int config_get(int id, GVariant **data, const struct sr_dev_inst *sdi, *data = g_variant_new_uint64(devc->cur_samplerate); break; case SR_CONF_LIMIT_SAMPLES: - *data = g_variant_new_uint64(devc->limit_samples); + *data = g_variant_new_uint64(devc->limit_samples_show); break; case SR_CONF_LIMIT_MSEC: *data = g_variant_new_uint64(devc->limit_msec); @@ -546,7 +574,7 @@ static int config_set(int id, GVariant *data, struct sr_dev_inst *sdi, struct sr_channel *ch, const struct sr_channel_group *cg) { - uint16_t i; + uint16_t i, j; int ret; const char *stropt; struct sr_channel *probe; @@ -567,14 +595,19 @@ static int config_set(int id, GVariant *data, struct sr_dev_inst *sdi, devc->cur_samplerate); ret = SR_OK; } else if (id == SR_CONF_LIMIT_SAMPLES) { - devc->limit_msec = 0; - devc->limit_samples = g_variant_get_uint64(data); + devc->limit_msec = 0; + devc->limit_samples = g_variant_get_uint64(data); + devc->limit_samples_show = devc->limit_samples; + if (sdi->mode == DSO && en_ch_num(sdi) == 1) { + devc->limit_samples /= 2; + } sr_dbg("%s: setting limit_samples to %" PRIu64, __func__, devc->limit_samples); ret = SR_OK; } else if (id == SR_CONF_LIMIT_MSEC) { devc->limit_msec = g_variant_get_uint64(data); devc->limit_samples = 0; + devc->limit_samples_show = devc->limit_samples; sr_dbg("%s: setting limit_msec to %" PRIu64, __func__, devc->limit_msec); ret = SR_OK; @@ -590,6 +623,9 @@ static int config_set(int id, GVariant *data, struct sr_dev_inst *sdi, else sdi->channels = g_slist_append(sdi->channels, probe); } + devc->cur_samplerate = SR_MHZ(1); + devc->limit_samples = SR_MB(1); + devc->limit_samples_show = devc->limit_samples; } else if (sdi->mode == DSO) { sr_dev_probes_free(sdi); for (i = 0; i < DEMO_MAX_DSO_PROBES_NUM; i++) { @@ -603,10 +639,14 @@ static int config_set(int id, GVariant *data, struct sr_dev_inst *sdi, probe->trig_value = 0x80; probe->vpos = (probe->index == 0 ? 0.5 : -0.5)*probe->vdiv; sdi->channels = g_slist_append(sdi->channels, probe); + probe->ms_show = TRUE; + for (j = DSO_MS_BEGIN; j < DSO_MS_END; j++) + probe->ms_en[j] = default_ms_en[j]; } } devc->cur_samplerate = DEMO_MAX_DSO_SAMPLERATE / DEMO_MAX_DSO_PROBES_NUM; devc->limit_samples = DEMO_MAX_DSO_DEPTH / DEMO_MAX_DSO_PROBES_NUM; + devc->limit_samples_show = devc->limit_samples; } else if (sdi->mode == ANALOG) { sr_dev_probes_free(sdi); for (i = 0; i < DS_MAX_ANALOG_PROBES_NUM; i++) { @@ -618,6 +658,7 @@ static int config_set(int id, GVariant *data, struct sr_dev_inst *sdi, } devc->cur_samplerate = SR_HZ(100); devc->limit_samples = SR_KB(1); + devc->limit_samples_show = devc->limit_samples; } else { ret = SR_ERR; } @@ -980,6 +1021,8 @@ static int receive_data(int fd, int revents, const struct sr_dev_inst *sdi) dso.num_samples = sending_now; else dso.num_samples = devc->samples_counter; + if (en_ch_num(sdi) == 1) + dso.num_samples *= 2; dso.mq = SR_MQ_VOLTAGE; dso.unit = SR_UNIT_VOLT; dso.mqflags = SR_MQFLAG_AC; @@ -997,8 +1040,7 @@ static int receive_data(int fd, int revents, const struct sr_dev_inst *sdi) if (sdi->mode == DSO && !devc->instant) { devc->pre_index += sending_now; - if (sdi->mode == DSO && !devc->instant && - devc->pre_index >= devc->limit_samples) + if (devc->pre_index >= devc->limit_samples) devc->pre_index = 0; } @@ -1012,7 +1054,7 @@ static int receive_data(int fd, int revents, const struct sr_dev_inst *sdi) } } - if ((sdi->mode == LOGIC || devc->instant) && devc->limit_samples && + if ((sdi->mode != DSO || devc->instant) && devc->limit_samples && devc->samples_counter >= devc->limit_samples) { sr_info("Requested number of samples reached."); hw_dev_acquisition_stop(sdi, NULL); diff --git a/libsigrok4DSL/hwdriver.c b/libsigrok4DSL/hwdriver.c index e8917a84..15f3eb4f 100644 --- a/libsigrok4DSL/hwdriver.c +++ b/libsigrok4DSL/hwdriver.c @@ -76,6 +76,8 @@ static struct sr_config_info sr_config_info_data[] = { "Trigger slope", "Trigger slope", NULL}, {SR_CONF_TRIGGER_SOURCE, SR_T_UINT8, "triggersource", "Trigger source", "Trigger source", NULL}, + {SR_CONF_TRIGGER_CHANNEL, SR_T_UINT8, "triggerchannel", + "Trigger channel", "Trigger channel", NULL}, {SR_CONF_HORIZ_TRIGGERPOS, SR_T_UINT8, "horiz_triggerpos", "Horizontal trigger position", "Horizontal trigger position", NULL}, {SR_CONF_TRIGGER_HOLDOFF, SR_T_UINT64, "triggerholdoff", diff --git a/libsigrok4DSL/libsigrok.h b/libsigrok4DSL/libsigrok.h index f964940a..7626c5de 100644 --- a/libsigrok4DSL/libsigrok.h +++ b/libsigrok4DSL/libsigrok.h @@ -288,6 +288,18 @@ enum { SR_MQFLAG_SPL_PCT_OVER_ALARM = 0x10000, }; +enum DSO_MEASURE_TYPE { + DSO_MS_BEGIN = 0, + DSO_MS_FREQ, + DSO_MS_PERD, + DSO_MS_VMAX, + DSO_MS_VMIN, + DSO_MS_VRMS, + DSO_MS_VMEA, + DSO_MS_VP2P, + DSO_MS_END, +}; + struct sr_context; struct sr_datafeed_packet { @@ -327,6 +339,8 @@ struct sr_datafeed_dso { uint64_t mqflags; /** samplerate different from last packet */ gboolean samplerate_tog; + /** trig flag */ + gboolean trig_flag; /** The analog value(s). The data is interleaved according to * the probes list. */ void *data; @@ -575,6 +589,8 @@ struct sr_channel { uint8_t trig_value; int8_t comb_diff_top; int8_t comb_diff_bom; + gboolean ms_show; + gboolean ms_en[DSO_MS_END - DSO_MS_BEGIN]; }; /** Structure for groups of channels that have common properties. */ @@ -632,6 +648,9 @@ struct sr_status { gboolean stream_mode; uint32_t sample_divider; gboolean sample_divider_tog; + gboolean trig_flag; + + uint16_t pkt_id; }; enum { @@ -718,6 +737,9 @@ enum { /** Trigger source. */ SR_CONF_TRIGGER_SOURCE, + /** Trigger channel */ + SR_CONF_TRIGGER_CHANNEL, + /** Trigger Value. */ SR_CONF_TRIGGER_VALUE, @@ -1099,10 +1121,11 @@ struct ds_trigger { }; struct ds_trigger_pos { + uint32_t check_id; uint32_t real_pos; uint32_t ram_saddr; uint32_t remain_cnt; - unsigned char first_block[500]; + unsigned char first_block[496]; }; #include "proto.h" diff --git a/libsigrokdecode4DSL/.decoder.c.swp b/libsigrokdecode4DSL/.decoder.c.swp new file mode 100644 index 0000000000000000000000000000000000000000..cce209d61cafe8d080b1b77677e0217e146ec988 GIT binary patch literal 16384 zcmeI3U5p!76~~7{X-sK=P$3~2da@Cf?RxD^iiFhNE^#+b*6L>0^2a7y3Cwur+MZ-Q zcQkWn9XF(+h$iI$1W2@M1P?q=!3z%vsS*N#Dp3{jDO9Q!fdqvIq=G5|LV}Wn|CtZl z`;jI{RUgn;{$)LP?#H?Rd+wR>8K*P1UOhn{DSlYP>)o36)%R%Ye=O|PezTxyo|KA< zulEW%fun-!)zY+5Uk*e`cg#^~D7`bKqm6Y-3`@4v@vWXG&zQoHrYJ>!yrOaa9@84B z0;vKwuE1^DJ%qB@n&hwRDo21RDo21RDo21RDo21RDo21{|g0_dy94z zGr0+T0lX#VDqd&t`HS)K6&!yOpEIbybuQyr;_svJ@qcjqB^>`FK7I|yPvH0hI_COs z1qAlv10ScaRDo21RDo21RDo21RDo21RDo21RDo21RDo21H%|fG)UGX;PYSutb@Ja*}aAQ2jFS&4e%8BI`|ss0t4Iy4uaqBfvwyN;3PN<4uG4%Ki{rte+Pd9e*!OqKY&Z% zF>nEV0#raAPOp^ zdc9rSXfCg<@(HU;*{nNio0g$0B?fKXZ~MyQBH<`+RG=A0ABdR(-Q}7_e|C|NaydE> zb+-0Ej?ST*H*Pe`oQ!3e+egAsYzC&%YY%kWmPXdM9ul&fh0%eb$oI^)(bpYE*ty(1 z(q}hnS|&52lWqBmhL-9R>p{X61E}B^VUih5y+FEB9(snRJeKgh>`cv*N*dCp_?I;F zA)nt|)9M6DkmZvj70sX(3x1$BI*R_=OMPLS8B2}1vXqJPm}70!3|w2Zj|)}B+HlQy zZft0QEViR7J<3|BJvUDlEszrol3G4W?!G(hcC!9!4}`LyAn=^fBe*1!Ddnj$aMg&W zhA2|Lq(we5-R+cV4XD4(F3jUTSd)YJ>=si4ZFRj&W~ZkAv!psHmIx~p*$HVQ&BoeJ zO(#-#tBT=V);gyJ)&n}1v1Sb{o72m}I#k7yj@NwaY{s&!%0x4tWS3MQ%dJMPAm0^+ z)wP5<(Gm8=gDva+>kP)~S&ky8SJ<+oSaqz{PH(3?Zw#DD20+0!cj1C?Q_pMXFqPwt z6?XT0*wD^tzS5Ot5RcjuYV1Q$@VP^}Y zL+l4=%-$Am1N3PO+$@S_7cF$g`yJaO!)?~B{ES%S`}ZJF`>qbb5djrRoQ>49Pq2e5l z!=iYO&Au3if-ssW6pWpLsz(ciP0Q*Kbl}L)m4RaxlWtty(+5z4xTES(wiON;`PQUo zCD}W$iIaA4UpU{S!mdLXwNgGmy-10uQ`$s}n#eY-3d^6}l)K4rc!vz3dtpPwlKO?I zDLaC6526IFD?KGl7Q~jiiM7ffJs`)xZ~%2ym>t(%|yRRba*Hc)B-Ag1zNf%F=Db zN3%G?ftp=@pr38~!uKudz_k#q=h%7AY4P6cb)>X~?!=cdTpilnO{_SZMn{BWVtq{Z zLJiI@;vinDSJ!dDh=L2{2hwa$|HyM5jR}PTE+^CFMPt{FI0$UDb$-WSWB-We&mhr<_oA=euUXqPyiY|U98@SZ-+$1v4_N%=gxlG(WJ3Trrgx(x}2po2{@FPYf z+?-r**sf)ZsMCXpF?L`ITIk_dyT$&ZHZGaj_A}mMf9BdJXTyEt=r(E*L_B7Bd;{T2 z8e${zhQ>Z?*VY={#g8 zLjWrhRm13paRg@2SIWJ&R2mM4#hw!srPsqRkqzO%WlD>@YiUCUG>x?@}0^sr079AIk@N}DMK?j}CO4L!jZr84WK6RTv3p!D$FD;i> zo8|kfE7j%($D(7^=4z$Upkr%wDpRdoZ&sIDE9E-XT6M@&!Q>j46oLyIj+;C>S;akd zt4Zb64LVt_*U{g`e5i`VS+OacFcet>*Tx+Ii|gqQZntEYdkCXlEF4D=6K)jUJes&7 zhb{XOw-l^5Ddi;=)niG_*^svH{+J!n(c-7jUPR*l;dX) kzL=yew?BWtx9KEX%>CmrSDfxW!1@^clrPHT;_=3R0piw^0{{R3 literal 0 HcmV?d00001 diff --git a/libsigrokdecode4DSL/.gitignore b/libsigrokdecode4DSL/.gitignore new file mode 100644 index 00000000..f6eb0b8c --- /dev/null +++ b/libsigrokdecode4DSL/.gitignore @@ -0,0 +1,38 @@ +# autotools cruft +/INSTALL +/Makefile.in +/aclocal.m4 +/autom4te.cache/ +/autostuff/ +/configure +/configure.lineno +/m4/libtool.m4 +/m4/lt*.m4 + +# Editor/IDE cruft +*.kate-swp +*~ +/*.kdev4 +/Makefile.am.user + +# Configure/build cruft +*.[ao] +*.l[ao] +.deps/ +.dirstamp +.libs/ +/ChangeLog +/Makefile +/config.* +/libsigrokdecode-*.tar.* +/libsigrokdecode.pc +/libtool +/version.h +__pycache__/ +stamp-h? + +# Files generated by the testsuite +/test-suite.log +/tests/*.log +/tests/*.trs +/tests/main diff --git a/libsigrokdecode4DSL/.type_decoder.c.swp b/libsigrokdecode4DSL/.type_decoder.c.swp new file mode 100644 index 0000000000000000000000000000000000000000..6ca9f4e887c9acae23b4993cdd8105bcb074fe4c GIT binary patch literal 16384 zcmeHOTWlj&89u!(p%*H6K&nvtulFK5b?n^~(XyAQ-o)88n{_PP$&xP1FrGQKXLmef zX3oTQ(}kr_sg<}^An^bdP#!=+2oRSRfdmglMLh6EQ6(fmeFR?u`_GIMJIXdG zPzk|U`r?^${r~U(&+R`>>&R;596eBYr@-efA-=uuhSft4+$o+pAw=N&ij#NyM6JkE z(Qc)LD0cmxESjD@EDrqOa&e)t>c~OSby}g*3H-~pw0v6@)vzy(_&F$8yEmJbNEt{O z*b@VHi2G-c9yyTNPj7qc%ARVZg;EAm22uu622uu622uu622uu62L5*#Q2m?5HK=nV zz(@ES5?sS)9nTC-`v=~K`2IukeiObA0uq^=pMFvXQU+26QU+26QU+26QU+26QU+26 zQU+26QU?Be88Gem9Dh3p0C4}$F@RU!BE*Zp3&5v=PXJ}$)jNfF4)_f4N#HCn1-y8N z5T6G=2h@Olz)ir7z@Kgx;^0DcNw z1q|R};6dPS;N^WnJOexdJPy1ccsuaYtwMYe_!#gJ;41Ji@E~wE@bWD}d>MEWcmk*b zcLKk;QPSSz=weza1mGl=7GC_zrc3i z0X_I&k7p0sZ1m3e!6eBj*AUuVW(nfhD+x}s7&Qp;8c<2d+Rb# zNekJrdI@a?Gf&gDlc(BnBo3^V7Xl_7v*_~K<1n-C0cJrzbdvA}nelgK&h$J($ww4Y zM~~xKnuH{8LMI2)QZThU0@8#e%ySZB?#IK1wd*q`uFhfEw zZ9nKqdq>yOuDD0v+LDD_Csgow)8^^$h!#Dlc#JHc)lT=? zBWEX7Kn1*$`+h@p!6;cmA4WV#Vm)$NAf^R2a4bX2(&l?il<#$nQn-Y##3xwPU5wv$0$Lw7){D)HKQu<{7FY2+TF?cWT}hK#hVe4PuCl2oMz6BEj@N=O`a}b=7fp!R45cKUYuB7r+F;#@&ec5 zWsP5TExeDdX*(_$dRg37&Nlo=^)YQ@$@G!8we-bP+rPNVZs|0>jO}5ig$S8syK=7F zEHTrg_jvm272dVw>sGz#SFz}xT4b+^lx;7 zZe&Y30i$ttSm@4erDnGM`nS-{Tt9P$r@X*3U6djyvB*G@$#pJ+|I)T|M5LMcQDHr`n zXwg-wf2>#>3le_r>iL1h##fn+8|r?~mFx3oO4VlRRAs5sTqD?X zvC^!T8x2}qu2YF>rFye6zp_-SQ*EUVn#xeF0i{$|a)-Q!IbXqseWgjI>KdId)$3?) z?YL$IzCdzadR&^EUf*?~637iqPq9+mg7&I}IQA|Z(_i4AT@-cVnq9xo9h!<9o*7-2 zHw19NcI4(0#k0s9`cXiBU+?xX)Q9AnFg;--8)5^f^<_C01Bl=7+X{l<&5t}R=qQjU ziM{?-1-gGkd%|%Z^svMb0(DT3TSE^$3<;b+Uei8=tz@4q7IvXmYq*oc9+;2W|mg!Fm5jz|+8|0FDE!0SAEt0LKIN0k;Cb!TJArpbOj!{2gcgSAgFEzXg5) zdiTwwBYkozAEh94!1=asAukjEbM2@Mwaie)(KiksaJjZ?cDR+= z&}h~cmrAD_#zJ|qw6fHs$Ho&+l^SIZBP7+(kYjPg9#uzyd)!$hvgDFd9E;kLB94O; zk}@nnZMbR%%lZyPhY*ZPwbIO1SC*FYbR;J@NM5T~Rv||d!GpNqcqBq!C!qt5YJeuq z5mzWR%R0WyWNpOFC5`5ZA2{kdm8mB|U!2W7IY{Ci9KDccFLMRrZDdB_O9TaRIX1r- zd6qtvjyNCX=Lun(GsbGEUaFsNJf>gHme(anAz>gX8yX11=3N^vk;Hv|Dq{1{hCd;ck^o+-$NSCvGdp z5hygM+u>)UvkqA~q#4J$5_dW|etXE_IKK1XYc5t%U&dyB#g>ocIaZ~^ + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + 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 3 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, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/libsigrokdecode4DSL/Doxyfile b/libsigrokdecode4DSL/Doxyfile new file mode 100644 index 00000000..a53ec1d9 --- /dev/null +++ b/libsigrokdecode4DSL/Doxyfile @@ -0,0 +1,2318 @@ +# Doxyfile 1.8.6 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project. +# +# All text after a double hash (##) is considered a comment and is placed in +# front of the TAG it is preceding. +# +# All text after a single hash (#) is considered a comment and will be ignored. +# The format is: +# TAG = value [value, ...] +# For lists, items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (\" \"). + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the config file +# that follow. The default is UTF-8 which is also the encoding used for all text +# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv +# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv +# for the list of possible encodings. +# The default value is: UTF-8. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by +# double-quotes, unless you are using Doxywizard) that should identify the +# project for which the documentation is generated. This name is used in the +# title of most generated pages and in a few other places. +# The default value is: My Project. + +PROJECT_NAME = "libsigrokdecode" + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. This +# could be handy for archiving the generated documentation or if some version +# control system is used. + +PROJECT_NUMBER = "unreleased development snapshot" + +# Using the PROJECT_BRIEF tag one can provide an optional one line description +# for a project that appears at the top of each page and should give viewer a +# quick idea about the purpose of the project. Keep the description short. + +PROJECT_BRIEF = "sigrok protocol decoding library" + +# With the PROJECT_LOGO tag one can specify an logo or icon that is included in +# the documentation. The maximum height of the logo should not exceed 55 pixels +# and the maximum width should not exceed 200 pixels. Doxygen will copy the logo +# to the output directory. + +PROJECT_LOGO = contrib/sigrok-logo-notext.png + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path +# into which the generated documentation will be written. If a relative path is +# entered, it will be relative to the location where doxygen was started. If +# left blank the current directory will be used. + +OUTPUT_DIRECTORY = doxy + +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 4096 sub- +# directories (in 2 levels) under the output directory of each output format and +# will distribute the generated files over these directories. Enabling this +# option can be useful when feeding doxygen a huge amount of source files, where +# putting all generated files in the same directory would otherwise causes +# performance problems for the file system. +# The default value is: NO. + +CREATE_SUBDIRS = YES + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, +# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), +# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, +# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), +# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, +# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, +# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, +# Ukrainian and Vietnamese. +# The default value is: English. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES doxygen will include brief member +# descriptions after the members that are listed in the file and class +# documentation (similar to Javadoc). Set to NO to disable this. +# The default value is: YES. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES doxygen will prepend the brief +# description of a member or function before the detailed description +# +# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. +# The default value is: YES. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator that is +# used to form the text in various listings. Each string in this list, if found +# as the leading text of the brief description, will be stripped from the text +# and the result, after processing the whole list, is used as the annotated +# text. Otherwise, the brief description is used as-is. If left blank, the +# following values are used ($name is automatically replaced with the name of +# the entity):The $name class, The $name widget, The $name file, is, provides, +# specifies, contains, represents, a, an and the. + +ABBREVIATE_BRIEF = + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# doxygen will generate a detailed section even if there is only a brief +# description. +# The default value is: NO. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. +# The default value is: NO. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES doxygen will prepend the full path +# before files name in the file list and in the header files. If set to NO the +# shortest path that makes the file name unique will be used +# The default value is: YES. + +FULL_PATH_NAMES = YES + +# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. +# Stripping is only done if one of the specified strings matches the left-hand +# part of the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the path to +# strip. +# +# Note that you can specify absolute paths here, but also relative paths, which +# will be relative from the directory where doxygen is started. +# This tag requires that the tag FULL_PATH_NAMES is set to YES. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the +# path mentioned in the documentation of a class, which tells the reader which +# header file to include in order to use a class. If left blank only the name of +# the header file containing the class definition is used. Otherwise one should +# specify the list of include paths that are normally passed to the compiler +# using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but +# less readable) file names. This can be useful is your file systems doesn't +# support long names like on DOS, Mac, or CD-ROM. +# The default value is: NO. + +SHORT_NAMES = YES + +# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the +# first line (until the first dot) of a Javadoc-style comment as the brief +# description. If set to NO, the Javadoc-style will behave just like regular Qt- +# style comments (thus requiring an explicit @brief command for a brief +# description.) +# The default value is: NO. + +JAVADOC_AUTOBRIEF = YES + +# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first +# line (until the first dot) of a Qt-style comment as the brief description. If +# set to NO, the Qt-style will behave just like regular Qt-style comments (thus +# requiring an explicit \brief command for a brief description.) +# The default value is: NO. + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a +# multi-line C++ special comment block (i.e. a block of //! or /// comments) as +# a brief description. This used to be the default behavior. The new default is +# to treat a multi-line C++ comment block as a detailed description. Set this +# tag to YES if you prefer the old behavior instead. +# +# Note that setting this tag to YES also means that rational rose comments are +# not recognized any more. +# The default value is: NO. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the +# documentation from any documented member that it re-implements. +# The default value is: YES. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce a +# new page for each member. If set to NO, the documentation of a member will be +# part of the file/class/namespace that contains it. +# The default value is: NO. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen +# uses this value to replace tabs by spaces in code fragments. +# Minimum value: 1, maximum value: 16, default value: 4. + +TAB_SIZE = 8 + +# This tag can be used to specify a number of aliases that act as commands in +# the documentation. An alias has the form: +# name=value +# For example adding +# "sideeffect=@par Side Effects:\n" +# will allow you to put the command \sideeffect (or @sideeffect) in the +# documentation, which will result in a user-defined paragraph with heading +# "Side Effects:". You can put \n's in the value part of an alias to insert +# newlines. + +ALIASES = + +# This tag can be used to specify a number of word-keyword mappings (TCL only). +# A mapping has the form "name=value". For example adding "class=itcl::class" +# will allow you to use the command class in the itcl::class meaning. + +TCL_SUBST = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources +# only. Doxygen will then generate output that is more tailored for C. For +# instance, some of the names that are used will be different. The list of all +# members will be omitted, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_FOR_C = YES + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or +# Python sources only. Doxygen will then generate output that is more tailored +# for that language. For instance, namespaces will be presented as packages, +# qualified scopes will look different, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources. Doxygen will then generate output that is tailored for Fortran. +# The default value is: NO. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for VHDL. +# The default value is: NO. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given +# extension. Doxygen has a built-in mapping, but you can override or extend it +# using this tag. The format is ext=language, where ext is a file extension, and +# language is one of the parsers supported by doxygen: IDL, Java, Javascript, +# C#, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL. For instance to make +# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C +# (default is Fortran), use: inc=Fortran f=C. +# +# Note For files without extension you can use no_extension as a placeholder. +# +# Note that for custom extensions you also need to set FILE_PATTERNS otherwise +# the files are not read by doxygen. + +EXTENSION_MAPPING = + +# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments +# according to the Markdown format, which allows for more readable +# documentation. See http://daringfireball.net/projects/markdown/ for details. +# The output of markdown processing is further processed by doxygen, so you can +# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in +# case of backward compatibilities issues. +# The default value is: YES. + +MARKDOWN_SUPPORT = YES + +# When enabled doxygen tries to link words that correspond to documented +# classes, or namespaces to their corresponding documentation. Such a link can +# be prevented in individual cases by by putting a % sign in front of the word +# or globally by setting AUTOLINK_SUPPORT to NO. +# The default value is: YES. + +AUTOLINK_SUPPORT = YES + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should set this +# tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); +# versus func(std::string) {}). This also make the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. +# The default value is: NO. + +BUILTIN_STL_SUPPORT = NO + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. +# The default value is: NO. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip (see: +# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen +# will parse them like normal C++ but will assume all classes use public instead +# of private inheritance when no explicit protection keyword is present. +# The default value is: NO. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate +# getter and setter methods for a property. Setting this option to YES will make +# doxygen to replace the get and set methods by a property in the documentation. +# This will only work if the methods are indeed getting or setting a simple +# type. If this is not the case, or you want to show the methods anyway, you +# should set this option to NO. +# The default value is: YES. + +IDL_PROPERTY_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. +# The default value is: NO. + +DISTRIBUTE_GROUP_DOC = NO + +# Set the SUBGROUPING tag to YES to allow class member groups of the same type +# (for instance a group of public functions) to be put as a subgroup of that +# type (e.g. under the Public Functions section). Set it to NO to prevent +# subgrouping. Alternatively, this can be done per class using the +# \nosubgrouping command. +# The default value is: YES. + +SUBGROUPING = YES + +# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions +# are shown inside the group in which they are included (e.g. using \ingroup) +# instead of on a separate page (for HTML and Man pages) or section (for LaTeX +# and RTF). +# +# Note that this feature does not work in combination with +# SEPARATE_MEMBER_PAGES. +# The default value is: NO. + +INLINE_GROUPED_CLASSES = NO + +# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions +# with only public data fields or simple typedef fields will be shown inline in +# the documentation of the scope in which they are defined (i.e. file, +# namespace, or group documentation), provided this scope is documented. If set +# to NO, structs, classes, and unions are shown on a separate page (for HTML and +# Man pages) or section (for LaTeX and RTF). +# The default value is: NO. + +INLINE_SIMPLE_STRUCTS = NO + +# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or +# enum is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically be +# useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. +# The default value is: NO. + +TYPEDEF_HIDES_STRUCT = NO + +# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This +# cache is used to resolve symbols given their name and scope. Since this can be +# an expensive process and often the same symbol appears multiple times in the +# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small +# doxygen will become slower. If the cache is too large, memory is wasted. The +# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range +# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 +# symbols. At the end of a run doxygen will report the cache usage and suggest +# the optimal cache size from a speed point of view. +# Minimum value: 0, maximum value: 9, default value: 0. + +LOOKUP_CACHE_SIZE = 0 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. Private +# class members and static file members will be hidden unless the +# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. +# Note: This will also disable the warnings about undocumented members that are +# normally produced when WARNINGS is set to YES. +# The default value is: NO. + +EXTRACT_ALL = YES + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class will +# be included in the documentation. +# The default value is: NO. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal +# scope will be included in the documentation. +# The default value is: NO. + +EXTRACT_PACKAGE = NO + +# If the EXTRACT_STATIC tag is set to YES all static members of a file will be +# included in the documentation. +# The default value is: NO. + +EXTRACT_STATIC = NO + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) defined +# locally in source files will be included in the documentation. If set to NO +# only classes defined in header files are included. Does not have any effect +# for Java sources. +# The default value is: YES. + +EXTRACT_LOCAL_CLASSES = NO + +# This flag is only useful for Objective-C code. When set to YES local methods, +# which are defined in the implementation section but not in the interface are +# included in the documentation. If set to NO only methods in the interface are +# included. +# The default value is: NO. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base name of +# the file that contains the anonymous namespace. By default anonymous namespace +# are hidden. +# The default value is: NO. + +EXTRACT_ANON_NSPACES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all +# undocumented members inside documented classes or files. If set to NO these +# members will be included in the various overviews, but no documentation +# section is generated. This option has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. If set +# to NO these classes will be included in the various overviews. This option has +# no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend +# (class|struct|union) declarations. If set to NO these declarations will be +# included in the documentation. +# The default value is: NO. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any +# documentation blocks found inside the body of a function. If set to NO these +# blocks will be appended to the function's detailed documentation block. +# The default value is: NO. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation that is typed after a +# \internal command is included. If the tag is set to NO then the documentation +# will be excluded. Set it to YES to include the internal documentation. +# The default value is: NO. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file +# names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. +# The default value is: system dependent. + +CASE_SENSE_NAMES = NO + +# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with +# their full class and namespace scopes in the documentation. If set to YES the +# scope will be hidden. +# The default value is: NO. + +HIDE_SCOPE_NAMES = NO + +# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of +# the files that are included by a file in the documentation of that file. +# The default value is: YES. + +SHOW_INCLUDE_FILES = YES + +# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each +# grouped member an include statement to the documentation, telling the reader +# which file to include in order to use the member. +# The default value is: NO. + +SHOW_GROUPED_MEMB_INC = NO + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include +# files with double quotes in the documentation rather than with sharp brackets. +# The default value is: NO. + +FORCE_LOCAL_INCLUDES = NO + +# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the +# documentation for inline members. +# The default value is: YES. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the +# (detailed) documentation of file and class members alphabetically by member +# name. If set to NO the members will appear in declaration order. +# The default value is: YES. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief +# descriptions of file, namespace and class members alphabetically by member +# name. If set to NO the members will appear in declaration order. Note that +# this will also influence the order of the classes in the class list. +# The default value is: NO. + +SORT_BRIEF_DOCS = NO + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the +# (brief and detailed) documentation of class members so that constructors and +# destructors are listed first. If set to NO the constructors will appear in the +# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. +# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief +# member documentation. +# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting +# detailed member documentation. +# The default value is: NO. + +SORT_MEMBERS_CTORS_1ST = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy +# of group names into alphabetical order. If set to NO the group names will +# appear in their defined order. +# The default value is: NO. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by +# fully-qualified names, including namespaces. If set to NO, the class list will +# be sorted only by class name, not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the alphabetical +# list. +# The default value is: NO. + +SORT_BY_SCOPE_NAME = NO + +# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper +# type resolution of all parameters of a function it will reject a match between +# the prototype and the implementation of a member function even if there is +# only one candidate or it is obvious which candidate to choose by doing a +# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still +# accept a match between prototype and implementation in such cases. +# The default value is: NO. + +STRICT_PROTO_MATCHING = NO + +# The GENERATE_TODOLIST tag can be used to enable ( YES) or disable ( NO) the +# todo list. This list is created by putting \todo commands in the +# documentation. +# The default value is: YES. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable ( YES) or disable ( NO) the +# test list. This list is created by putting \test commands in the +# documentation. +# The default value is: YES. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable ( YES) or disable ( NO) the bug +# list. This list is created by putting \bug commands in the documentation. +# The default value is: YES. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable ( YES) or disable ( NO) +# the deprecated list. This list is created by putting \deprecated commands in +# the documentation. +# The default value is: YES. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional documentation +# sections, marked by \if ... \endif and \cond +# ... \endcond blocks. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the +# initial value of a variable or macro / define can have for it to appear in the +# documentation. If the initializer consists of more lines than specified here +# it will be hidden. Use a value of 0 to hide initializers completely. The +# appearance of the value of individual variables and macros / defines can be +# controlled using \showinitializer or \hideinitializer command in the +# documentation regardless of this setting. +# Minimum value: 0, maximum value: 10000, default value: 30. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at +# the bottom of the documentation of classes and structs. If set to YES the list +# will mention the files that were used to generate the documentation. +# The default value is: YES. + +SHOW_USED_FILES = YES + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This +# will remove the Files entry from the Quick Index and from the Folder Tree View +# (if specified). +# The default value is: YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces +# page. This will remove the Namespaces entry from the Quick Index and from the +# Folder Tree View (if specified). +# The default value is: YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command command input-file, where command is the value of the +# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided +# by doxygen. Whatever the program writes to standard output is used as the file +# version. For an example see the documentation. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. To create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. You can +# optionally specify a file name after the option, if omitted DoxygenLayout.xml +# will be used as the name of the layout file. +# +# Note that if you run doxygen from a directory containing a file called +# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE +# tag is left empty. + +LAYOUT_FILE = + +# The CITE_BIB_FILES tag can be used to specify one or more bib files containing +# the reference definitions. This must be a list of .bib files. The .bib +# extension is automatically appended if omitted. This requires the bibtex tool +# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info. +# For LaTeX the style of the bibliography can be controlled using +# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the +# search path. Do not use file names with spaces, bibtex cannot handle them. See +# also \cite for info how to create references. + +CITE_BIB_FILES = + +#--------------------------------------------------------------------------- +# Configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated to +# standard output by doxygen. If QUIET is set to YES this implies that the +# messages are off. +# The default value is: NO. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated to standard error ( stderr) by doxygen. If WARNINGS is set to YES +# this implies that the warnings are on. +# +# Tip: Turn warnings on while writing the documentation. +# The default value is: YES. + +WARNINGS = YES + +# If the WARN_IF_UNDOCUMENTED tag is set to YES, then doxygen will generate +# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag +# will automatically be disabled. +# The default value is: YES. + +WARN_IF_UNDOCUMENTED = YES + +# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some parameters +# in a documented function, or documenting parameters that don't exist or using +# markup commands wrongly. +# The default value is: YES. + +WARN_IF_DOC_ERROR = YES + +# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that +# are documented, but have no documentation for their parameters or return +# value. If set to NO doxygen will only warn about wrong or incomplete parameter +# documentation, but not about the absence of documentation. +# The default value is: NO. + +WARN_NO_PARAMDOC = YES + +# The WARN_FORMAT tag determines the format of the warning messages that doxygen +# can produce. The string should contain the $file, $line, and $text tags, which +# will be replaced by the file and line number from which the warning originated +# and the warning text. Optionally the format may contain $version, which will +# be replaced by the version of the file (if it could be obtained via +# FILE_VERSION_FILTER) +# The default value is: $file:$line: $text. + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning and error +# messages should be written. If left blank the output is written to standard +# error (stderr). + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# Configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag is used to specify the files and/or directories that contain +# documented source files. You may enter file names like myfile.cpp or +# directories like /usr/src/myproject. Separate the files or directories with +# spaces. +# Note: If this tag is empty the current directory is searched. + +INPUT = . + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses +# libiconv (or the iconv built into libc) for the transcoding. See the libiconv +# documentation (see: http://www.gnu.org/software/libiconv) for the list of +# possible encodings. +# The default value is: UTF-8. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and +# *.h) to filter out the source-files in the directories. If left blank the +# following patterns are tested:*.c, *.cc, *.cxx, *.cpp, *.c++, *.java, *.ii, +# *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp, +# *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown, +# *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf, +# *.qsf, *.as and *.js. + +FILE_PATTERNS = + +# The RECURSIVE tag can be used to specify whether or not subdirectories should +# be searched for input files as well. +# The default value is: NO. + +RECURSIVE = YES + +# The EXCLUDE tag can be used to specify files and/or directories that should be +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. +# +# Note that relative paths are relative to the directory from which doxygen is +# run. + +EXCLUDE = config.h libsigrokdecode-internal.h exception.c \ + module_sigrokdecode.c type_decoder.c type_logic.c \ + util.c + +# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or +# directories that are symbolic links (a Unix file system feature) are excluded +# from the input. +# The default value is: NO. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories for example use the pattern */test/* + +# Ignore the following files and directories (see also EXCLUDE above): +# - config.h: Non-public stuff, the file doesn't get installed. +# - libsigrokdecode-internal.h: Non-public stuff, the file isn't installed. +# - decoders/*: The decoders themselves don't contain public API. +# - exception.c: No public API stuff in there currently. +# - module_sigrokdecode.c: No public API stuff in there currently. +# - type_decoder.c: No public API stuff in there currently. +# - tyoe_logic.c: No public API stuff in there currently. +# - util.c: No public API stuff in there currently. +# - tests/*: Unit tests, no public API stuff in there. +# - doxy/*: Potentially already generated docs, should not be scanned. +# +EXCLUDE_PATTERNS = */decoders/* */tests/* */doxy/* + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories use the pattern */test/* + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or directories +# that contain example code fragments that are included (see the \include +# command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and +# *.h) to filter out the source-files in the directories. If left blank all +# files are included. + +EXAMPLE_PATTERNS = + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude commands +# irrespective of the value of the RECURSIVE tag. +# The default value is: NO. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or directories +# that contain images that are to be included in the documentation (see the +# \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command: +# +# +# +# where is the value of the INPUT_FILTER tag, and is the +# name of an input file. Doxygen will then use the output that the filter +# program writes to standard output. If FILTER_PATTERNS is specified, this tag +# will be ignored. +# +# Note that the filter must not add or remove lines; it is applied before the +# code is scanned, but not when the output code is generated. If lines are added +# or removed, the anchors will not be placed correctly. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: pattern=filter +# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how +# filters are used. If the FILTER_PATTERNS tag is empty or if none of the +# patterns match the file name, INPUT_FILTER is applied. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER ) will also be used to filter the input files that are used for +# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). +# The default value is: NO. + +FILTER_SOURCE_FILES = NO + +# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and +# it is also possible to disable source filtering for a specific pattern using +# *.ext= (so without naming a filter). +# This tag requires that the tag FILTER_SOURCE_FILES is set to YES. + +FILTER_SOURCE_PATTERNS = + +# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that +# is part of the input, its contents will be placed on the main page +# (index.html). This can be useful if you have a project on for instance GitHub +# and want to reuse the introduction page also for the doxygen output. + +USE_MDFILE_AS_MAINPAGE = + +#--------------------------------------------------------------------------- +# Configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will be +# generated. Documented entities will be cross-referenced with these sources. +# +# Note: To get rid of all source code in the generated output, make sure that +# also VERBATIM_HEADERS is set to NO. +# The default value is: NO. + +SOURCE_BROWSER = YES + +# Setting the INLINE_SOURCES tag to YES will include the body of functions, +# classes and enums directly into the documentation. +# The default value is: NO. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any +# special comment blocks from generated source code fragments. Normal C, C++ and +# Fortran comments will always remain visible. +# The default value is: YES. + +STRIP_CODE_COMMENTS = NO + +# If the REFERENCED_BY_RELATION tag is set to YES then for each documented +# function all documented functions referencing it will be listed. +# The default value is: NO. + +REFERENCED_BY_RELATION = YES + +# If the REFERENCES_RELATION tag is set to YES then for each documented function +# all documented entities called/used by that function will be listed. +# The default value is: NO. + +REFERENCES_RELATION = YES + +# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set +# to YES, then the hyperlinks from functions in REFERENCES_RELATION and +# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will +# link to the documentation. +# The default value is: YES. + +REFERENCES_LINK_SOURCE = YES + +# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the +# source code will show a tooltip with additional information such as prototype, +# brief description and links to the definition and documentation. Since this +# will make the HTML file larger and loading of large files a bit slower, you +# can opt to disable this feature. +# The default value is: YES. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +SOURCE_TOOLTIPS = YES + +# If the USE_HTAGS tag is set to YES then the references to source code will +# point to the HTML generated by the htags(1) tool instead of doxygen built-in +# source browser. The htags tool is part of GNU's global source tagging system +# (see http://www.gnu.org/software/global/global.html). You will need version +# 4.8.6 or higher. +# +# To use it do the following: +# - Install the latest version of global +# - Enable SOURCE_BROWSER and USE_HTAGS in the config file +# - Make sure the INPUT points to the root of the source tree +# - Run doxygen as normal +# +# Doxygen will invoke htags (and that will in turn invoke gtags), so these +# tools must be available from the command line (i.e. in the search path). +# +# The result: instead of the source browser generated by doxygen, the links to +# source code will now point to the output of htags. +# The default value is: NO. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a +# verbatim copy of the header file for each class for which an include is +# specified. Set to NO to disable this. +# See also: Section \class. +# The default value is: YES. + +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# Configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all +# compounds will be generated. Enable this if the project contains a lot of +# classes, structs, unions or interfaces. +# The default value is: YES. + +ALPHABETICAL_INDEX = YES + +# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in +# which the alphabetical index list will be split. +# Minimum value: 1, maximum value: 20, default value: 5. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all classes will +# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag +# can be used to specify a prefix (or a list of prefixes) that should be ignored +# while generating the index headers. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES doxygen will generate HTML output +# The default value is: YES. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_OUTPUT = html-api + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each +# generated HTML page (for example: .htm, .php, .asp). +# The default value is: .html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a user-defined HTML header file for +# each generated HTML page. If the tag is left blank doxygen will generate a +# standard header. +# +# To get valid HTML the header file that includes any scripts and style sheets +# that doxygen needs, which is dependent on the configuration options used (e.g. +# the setting GENERATE_TREEVIEW). It is highly recommended to start with a +# default header using +# doxygen -w html new_header.html new_footer.html new_stylesheet.css +# YourConfigFile +# and then modify the file new_header.html. See also section "Doxygen usage" +# for information on how to generate the default header that doxygen normally +# uses. +# Note: The header is subject to change so you typically have to regenerate the +# default header when upgrading to a newer version of doxygen. For a description +# of the possible markers and block names see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each +# generated HTML page. If the tag is left blank doxygen will generate a standard +# footer. See HTML_HEADER for more information on how to generate a default +# footer and what special commands can be used inside the footer. See also +# section "Doxygen usage" for information on how to generate the default footer +# that doxygen normally uses. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style +# sheet that is used by each HTML page. It can be used to fine-tune the look of +# the HTML output. If left blank doxygen will generate a default style sheet. +# See also section "Doxygen usage" for information on how to generate the style +# sheet that doxygen normally uses. +# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as +# it is more robust and this tag (HTML_STYLESHEET) will in the future become +# obsolete. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_STYLESHEET = + +# The HTML_EXTRA_STYLESHEET tag can be used to specify an additional user- +# defined cascading style sheet that is included after the standard style sheets +# created by doxygen. Using this option one can overrule certain style aspects. +# This is preferred over using HTML_STYLESHEET since it does not replace the +# standard style sheet and is therefor more robust against future updates. +# Doxygen will copy the style sheet file to the output directory. For an example +# see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_STYLESHEET = + +# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the HTML output directory. Note +# that these files will be copied to the base HTML output directory. Use the +# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these +# files. In the HTML_STYLESHEET file, use the file name only. Also note that the +# files will be copied as-is; there are no commands or markers available. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_FILES = + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen +# will adjust the colors in the stylesheet and background images according to +# this color. Hue is specified as an angle on a colorwheel, see +# http://en.wikipedia.org/wiki/Hue for more information. For instance the value +# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 +# purple, and 360 is red again. +# Minimum value: 0, maximum value: 359, default value: 220. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_HUE = 220 + +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors +# in the HTML output. For a value of 0 the output will use grayscales only. A +# value of 255 will produce the most vivid colors. +# Minimum value: 0, maximum value: 255, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_SAT = 100 + +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the +# luminance component of the colors in the HTML output. Values below 100 +# gradually make the output lighter, whereas values above 100 make the output +# darker. The value divided by 100 is the actual gamma applied, so 80 represents +# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not +# change the gamma. +# Minimum value: 40, maximum value: 240, default value: 80. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_GAMMA = 80 + +# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML +# page will contain the date and time when the page was generated. Setting this +# to NO can help when comparing the output of multiple runs. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_TIMESTAMP = YES + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_DYNAMIC_SECTIONS = YES + +# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries +# shown in the various tree structured indices initially; the user can expand +# and collapse entries dynamically later on. Doxygen will expand the tree to +# such a level that at most the specified number of entries are visible (unless +# a fully collapsed tree already exceeds this amount). So setting the number of +# entries 1 will produce a full collapsed tree by default. 0 is a special value +# representing an infinite number of entries and will result in a full expanded +# tree by default. +# Minimum value: 0, maximum value: 9999, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_INDEX_NUM_ENTRIES = 100 + +# If the GENERATE_DOCSET tag is set to YES, additional index files will be +# generated that can be used as input for Apple's Xcode 3 integrated development +# environment (see: http://developer.apple.com/tools/xcode/), introduced with +# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a +# Makefile in the HTML output directory. Running make will produce the docset in +# that directory and running make install will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at +# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html +# for more information. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_DOCSET = NO + +# This tag determines the name of the docset feed. A documentation feed provides +# an umbrella under which multiple documentation sets from a single provider +# (such as a company or product suite) can be grouped. +# The default value is: Doxygen generated docs. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# This tag specifies a string that should uniquely identify the documentation +# set bundle. This should be a reverse domain-name style string, e.g. +# com.mycompany.MyDocSet. Doxygen will append .docset to the name. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify +# the documentation publisher. This should be a reverse domain-name style +# string, e.g. com.mycompany.MyDocSet.documentation. +# The default value is: org.doxygen.Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_ID = org.doxygen.Publisher + +# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. +# The default value is: Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_NAME = Publisher + +# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three +# additional HTML index files: index.hhp, index.hhc, and index.hhk. The +# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop +# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on +# Windows. +# +# The HTML Help Workshop contains a compiler that can convert all HTML output +# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML +# files are now used as the Windows 98 help format, and will replace the old +# Windows help format (.hlp) on all Windows platforms in the future. Compressed +# HTML files also contain an index, a table of contents, and you can search for +# words in the documentation. The HTML workshop also contains a viewer for +# compressed HTML files. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_HTMLHELP = NO + +# The CHM_FILE tag can be used to specify the file name of the resulting .chm +# file. You can add a path in front of the file if the result should not be +# written to the html output directory. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_FILE = + +# The HHC_LOCATION tag can be used to specify the location (absolute path +# including file name) of the HTML help compiler ( hhc.exe). If non-empty +# doxygen will try to run the HTML help compiler on the generated index.hhp. +# The file has to be specified with full path. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +HHC_LOCATION = + +# The GENERATE_CHI flag controls if a separate .chi index file is generated ( +# YES) or that it should be included in the master .chm file ( NO). +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +GENERATE_CHI = NO + +# The CHM_INDEX_ENCODING is used to encode HtmlHelp index ( hhk), content ( hhc) +# and project file content. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_INDEX_ENCODING = + +# The BINARY_TOC flag controls whether a binary table of contents is generated ( +# YES) or a normal table of contents ( NO) in the .chm file. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members to +# the table of contents of the HTML help documentation and to the tree view. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +TOC_EXPAND = NO + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that +# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help +# (.qch) of the generated HTML documentation. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify +# the file name of the resulting .qch file. The path specified is relative to +# the HTML output folder. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help +# Project output. For more information please see Qt Help Project / Namespace +# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace). +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_NAMESPACE = org.doxygen.Project + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt +# Help Project output. For more information please see Qt Help Project / Virtual +# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual- +# folders). +# The default value is: doc. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_VIRTUAL_FOLDER = doc + +# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom +# filter to add. For more information please see Qt Help Project / Custom +# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- +# filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see Qt Help Project / Custom +# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- +# filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's filter section matches. Qt Help Project / Filter Attributes (see: +# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_SECT_FILTER_ATTRS = + +# The QHG_LOCATION tag can be used to specify the location of Qt's +# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the +# generated .qhp file. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHG_LOCATION = + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be +# generated, together with the HTML files, they form an Eclipse help plugin. To +# install this plugin and make it available under the help contents menu in +# Eclipse, the contents of the directory containing the HTML and XML files needs +# to be copied into the plugins directory of eclipse. The name of the directory +# within the plugins directory should be the same as the ECLIPSE_DOC_ID value. +# After copying Eclipse needs to be restarted before the help appears. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the Eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have this +# name. Each documentation set should have its own identifier. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. + +ECLIPSE_DOC_ID = org.doxygen.Project + +# If you want full control over the layout of the generated HTML pages it might +# be necessary to disable the index and replace it with your own. The +# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top +# of each HTML page. A value of NO enables the index and the value YES disables +# it. Since the tabs in the index contain the same information as the navigation +# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +DISABLE_INDEX = NO + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. If the tag +# value is set to YES, a side panel will be generated containing a tree-like +# index structure (just like the one that is generated for HTML Help). For this +# to work a browser that supports JavaScript, DHTML, CSS and frames is required +# (i.e. any modern browser). Windows users are probably better off using the +# HTML help feature. Via custom stylesheets (see HTML_EXTRA_STYLESHEET) one can +# further fine-tune the look of the index. As an example, the default style +# sheet generated by doxygen has an example that shows how to put an image at +# the root of the tree instead of the PROJECT_NAME. Since the tree basically has +# the same information as the tab index, you could consider setting +# DISABLE_INDEX to YES when enabling this option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_TREEVIEW = NO + +# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that +# doxygen will group on one line in the generated HTML documentation. +# +# Note that a value of 0 will completely suppress the enum values from appearing +# in the overview section. +# Minimum value: 0, maximum value: 20, default value: 4. +# This tag requires that the tag GENERATE_HTML is set to YES. + +ENUM_VALUES_PER_LINE = 1 + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used +# to set the initial width (in pixels) of the frame in which the tree is shown. +# Minimum value: 0, maximum value: 1500, default value: 250. +# This tag requires that the tag GENERATE_HTML is set to YES. + +TREEVIEW_WIDTH = 250 + +# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open links to +# external symbols imported via tag files in a separate window. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +EXT_LINKS_IN_WINDOW = NO + +# Use this tag to change the font size of LaTeX formulas included as images in +# the HTML documentation. When you change the font size after a successful +# doxygen run you need to manually remove any form_*.png images from the HTML +# output directory to force them to be regenerated. +# Minimum value: 8, maximum value: 50, default value: 10. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_FONTSIZE = 10 + +# Use the FORMULA_TRANPARENT tag to determine whether or not the images +# generated for formulas are transparent PNGs. Transparent PNGs are not +# supported properly for IE 6.0, but are supported on all modern browsers. +# +# Note that when changing this option you need to delete any form_*.png files in +# the HTML output directory before the changes have effect. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_TRANSPARENT = YES + +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see +# http://www.mathjax.org) which uses client side Javascript for the rendering +# instead of using prerendered bitmaps. Use this if you do not have LaTeX +# installed or if you want to formulas look prettier in the HTML output. When +# enabled you may also need to install MathJax separately and configure the path +# to it using the MATHJAX_RELPATH option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +USE_MATHJAX = NO + +# When MathJax is enabled you can set the default output format to be used for +# the MathJax output. See the MathJax site (see: +# http://docs.mathjax.org/en/latest/output.html) for more details. +# Possible values are: HTML-CSS (which is slower, but has the best +# compatibility), NativeMML (i.e. MathML) and SVG. +# The default value is: HTML-CSS. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_FORMAT = HTML-CSS + +# When MathJax is enabled you need to specify the location relative to the HTML +# output directory using the MATHJAX_RELPATH option. The destination directory +# should contain the MathJax.js script. For instance, if the mathjax directory +# is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax +# Content Delivery Network so you can quickly see the result without installing +# MathJax. However, it is strongly recommended to install a local copy of +# MathJax from http://www.mathjax.org before deployment. +# The default value is: http://cdn.mathjax.org/mathjax/latest. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest + +# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax +# extension names that should be enabled during MathJax rendering. For example +# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_EXTENSIONS = + +# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces +# of code that will be used on startup of the MathJax code. See the MathJax site +# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an +# example see the documentation. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_CODEFILE = + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box for +# the HTML output. The underlying search engine uses javascript and DHTML and +# should work on any modern browser. Note that when using HTML help +# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) +# there is already a search function so this one should typically be disabled. +# For large projects the javascript based search engine can be slow, then +# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to +# search using the keyboard; to jump to the search box use + S +# (what the is depends on the OS and browser, but it is typically +# , /sigrok project aims at creating a + * portable, cross-platform, Free/Libre/Open-Source signal analysis software + * suite that supports various device types (such as logic analyzers, + * oscilloscopes, multimeters, and more). + * + * libsigrokdecode is a + * shared library written in C which provides the basic API for (streaming) + * protocol decoding functionality. + * + * The protocol decoders + * are written in Python (>= 3.0). + * + * @section sec_api API reference + * + * See the "Modules" page for an introduction to various libsigrokdecode + * related topics and the detailed API documentation of the respective + * functions. + * + * You can also browse the API documentation by file, or review all + * data structures. + * + * @section sec_mailinglists Mailing lists + * + * There is one mailing list for sigrok/libsigrokdecode: sigrok-devel. + * + * @section sec_irc IRC + * + * You can find the sigrok developers in the + * \#sigrok + * IRC channel on Freenode. + * + * @section sec_website Website + * + * sigrok.org/wiki/Libsigrokdecode + */ + +/** + * @file + * + * Initializing and shutting down libsigrokdecode. + */ + +/** + * @defgroup grp_init Initialization + * + * Initializing and shutting down libsigrokdecode. + * + * Before using any of the libsigrokdecode functionality, srd_init() must + * be called to initialize the library. + * + * When libsigrokdecode functionality is no longer needed, srd_exit() should + * be called. + * + * @{ + */ + +/** + * Initialize libsigrokdecode. + * + * This initializes the Python interpreter, and creates and initializes + * a "sigrokdecode" Python module. + * + * Then, it searches for sigrok protocol decoders in the "decoders" + * subdirectory of the the libsigrokdecode installation directory. + * All decoders that are found are loaded into memory and added to an + * internal list of decoders, which can be queried via srd_decoder_list(). + * + * The caller is responsible for calling the clean-up function srd_exit(), + * which will properly shut down libsigrokdecode and free its allocated memory. + * + * Multiple calls to srd_init(), without calling srd_exit() in between, + * are not allowed. + * + * @param path Path to an extra directory containing protocol decoders + * which will be added to the Python sys.path. May be NULL. + * + * @return SRD_OK upon success, a (negative) error code otherwise. + * Upon Python errors, SRD_ERR_PYTHON is returned. If the decoders + * directory cannot be accessed, SRD_ERR_DECODERS_DIR is returned. + * If not enough memory could be allocated, SRD_ERR_MALLOC is returned. + * + * @since 0.1.0 + */ +SRD_API int srd_init(const char *path) +{ + int ret; + char *env_path; + + if (max_session_id != -1) { + srd_err("libsigrokdecode is already initialized."); + return SRD_ERR; + } + + srd_dbg("Initializing libsigrokdecode."); + + /* Add our own module to the list of built-in modules. */ + PyImport_AppendInittab("sigrokdecode", PyInit_sigrokdecode); + + /* Initialize the Python interpreter. */ + Py_Initialize(); + + /* Installed decoders. */ + if ((ret = srd_decoder_searchpath_add(DECODERS_DIR)) != SRD_OK) { + Py_Finalize(); + return ret; + } + + /* Path specified by the user. */ + if (path) { + if ((ret = srd_decoder_searchpath_add(path)) != SRD_OK) { + Py_Finalize(); + return ret; + } + } + + /* Environment variable overrides everything, for debugging. */ + if ((env_path = getenv("SIGROKDECODE_DIR"))) { + if ((ret = srd_decoder_searchpath_add(env_path)) != SRD_OK) { + Py_Finalize(); + return ret; + } + } + + max_session_id = 0; + + return SRD_OK; +} + +/** + * Shutdown libsigrokdecode. + * + * This frees all the memory allocated for protocol decoders and shuts down + * the Python interpreter. + * + * This function should only be called if there was a (successful!) invocation + * of srd_init() before. Calling this function multiple times in a row, without + * any successful srd_init() calls in between, is not allowed. + * + * @return SRD_OK upon success, a (negative) error code otherwise. + * + * @since 0.1.0 + */ +SRD_API int srd_exit(void) +{ + GSList *l; + + srd_dbg("Exiting libsigrokdecode."); + + for (l = sessions; l; l = l->next) + srd_session_destroy((struct srd_session *)l->data); + + srd_decoder_unload_all(); + g_slist_free_full(searchpaths, g_free); + searchpaths = NULL; + + /* Py_Finalize() returns void, any finalization errors are ignored. */ + Py_Finalize(); + + max_session_id = -1; + + return SRD_OK; +} + +/** + * Add an additional search directory for the protocol decoders. + * + * The specified directory is prepended (not appended!) to Python's sys.path, + * in order to search for sigrok protocol decoders in the specified + * directories first, and in the generic Python module directories (and in + * the current working directory) last. This avoids conflicts if there are + * Python modules which have the same name as a sigrok protocol decoder in + * sys.path or in the current working directory. + * + * @param path Path to the directory containing protocol decoders which shall + * be added to the Python sys.path, or NULL. + * + * @return SRD_OK upon success, a (negative) error code otherwise. + * + * @private + * + * @since 0.1.0 + */ +SRD_PRIV int srd_decoder_searchpath_add(const char *path) +{ + PyObject *py_cur_path, *py_item; + GString *new_path; + int wc_len, i; + wchar_t *wc_new_path; + char *item; + + srd_dbg("Adding '%s' to module path.", path); + + new_path = g_string_sized_new(256); + g_string_assign(new_path, path); + py_cur_path = PySys_GetObject("path"); + for (i = 0; i < PyList_Size(py_cur_path); i++) { + g_string_append(new_path, G_SEARCHPATH_SEPARATOR_S); + py_item = PyList_GetItem(py_cur_path, i); + if (!PyUnicode_Check(py_item)) + /* Shouldn't happen. */ + continue; + if (py_str_as_str(py_item, &item) != SRD_OK) + continue; + g_string_append(new_path, item); + g_free(item); + } + + /* Convert to wide chars. */ + wc_len = sizeof(wchar_t) * (new_path->len + 1); + wc_new_path = g_malloc(wc_len); + mbstowcs(wc_new_path, new_path->str, wc_len); + PySys_SetPath(wc_new_path); + g_string_free(new_path, TRUE); + g_free(wc_new_path); + searchpaths = g_slist_append(searchpaths, g_strdup(path)); + + return SRD_OK; +} + +/** @} */ diff --git a/libsigrokdecode4DSL/tests/core.c b/libsigrokdecode4DSL/tests/core.c new file mode 100644 index 00000000..3b26a2e1 --- /dev/null +++ b/libsigrokdecode4DSL/tests/core.c @@ -0,0 +1,105 @@ +/* + * This file is part of the libsigrokdecode project. + * + * Copyright (C) 2013 Uwe Hermann + * + * 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 +#include "../libsigrokdecode.h" /* First, to avoid compiler warning. */ +#include +#include +#include "lib.h" + +/* + * Check various basic init related things. + * + * - Check whether an srd_init() call with path == NULL works. + * If it returns != SRD_OK (or segfaults) this test will fail. + * + * - Check whether a subsequent srd_exit() works. + * If it returns != SRD_OK (or segfaults) this test will fail. + */ +START_TEST(test_init_exit) +{ + int ret; + + ret = srd_init(NULL); + fail_unless(ret == SRD_OK, "srd_init() failed: %d.", ret); + ret = srd_exit(); + fail_unless(ret == SRD_OK, "srd_exit() failed: %d.", ret); +} +END_TEST + +/* + * Check whether nested srd_init()/srd_exit() calls work/fail correctly. + * Two consecutive srd_init() calls without any srd_exit() inbetween are + * not allowed and should fail. However, two consecutive srd_exit() calls + * are currently allowed, the second one will just be a NOP basically. + */ +START_TEST(test_init_exit_2) +{ + int ret; + + ret = srd_init(NULL); + fail_unless(ret == SRD_OK, "srd_init() 1 failed: %d.", ret); + ret = srd_init(NULL); + fail_unless(ret != SRD_OK, "srd_init() 2 didn't fail: %d.", ret); + ret = srd_exit(); + fail_unless(ret == SRD_OK, "srd_exit() 2 failed: %d.", ret); + ret = srd_exit(); + fail_unless(ret == SRD_OK, "srd_exit() 1 failed: %d.", ret); +} +END_TEST + +/* + * Check whether three nested srd_init()/srd_exit() calls work/fail correctly. + */ +START_TEST(test_init_exit_3) +{ + int ret; + + ret = srd_init(NULL); + fail_unless(ret == SRD_OK, "srd_init() 1 failed: %d.", ret); + ret = srd_init(NULL); + fail_unless(ret != SRD_OK, "srd_init() 2 didn't fail: %d.", ret); + ret = srd_init(NULL); + fail_unless(ret != SRD_OK, "srd_init() 3 didn't fail: %d.", ret); + ret = srd_exit(); + fail_unless(ret == SRD_OK, "srd_exit() 3 failed: %d.", ret); + ret = srd_exit(); + fail_unless(ret == SRD_OK, "srd_exit() 2 failed: %d.", ret); + ret = srd_exit(); + fail_unless(ret == SRD_OK, "srd_exit() 1 failed: %d.", ret); +} +END_TEST + +Suite *suite_core(void) +{ + Suite *s; + TCase *tc; + + s = suite_create("core"); + + tc = tcase_create("init_exit"); + tcase_add_checked_fixture(tc, srdtest_setup, srdtest_teardown); + tcase_add_test(tc, test_init_exit); + tcase_add_test(tc, test_init_exit_2); + tcase_add_test(tc, test_init_exit_3); + suite_add_tcase(s, tc); + + return s; +} diff --git a/libsigrokdecode4DSL/tests/decoder.c b/libsigrokdecode4DSL/tests/decoder.c new file mode 100644 index 00000000..97d57678 --- /dev/null +++ b/libsigrokdecode4DSL/tests/decoder.c @@ -0,0 +1,346 @@ +/* + * This file is part of the libsigrokdecode project. + * + * Copyright (C) 2013 Uwe Hermann + * + * 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 +#include "../libsigrokdecode.h" /* First, to avoid compiler warning. */ +#include +#include +#include "lib.h" + +/* + * Check whether srd_decoder_load_all() works. + * If it returns != SRD_OK (or segfaults) this test will fail. + */ +START_TEST(test_load_all) +{ + int ret; + + srd_init(DECODERS_DIR); + ret = srd_decoder_load_all(); + fail_unless(ret == SRD_OK, "srd_decoder_load_all() failed: %d.", ret); + srd_exit(); +} +END_TEST + +/* + * Check whether srd_decoder_load_all() fails without prior srd_init(). + * If it returns != SRD_OK (or segfaults) this test will fail. + * See also: http://sigrok.org/bugzilla/show_bug.cgi?id=178 + */ +START_TEST(test_load_all_no_init) +{ + int ret; + + ret = srd_decoder_load_all(); + fail_unless(ret != SRD_OK, "srd_decoder_load_all() didn't fail properly."); +} +END_TEST + +/* + * Check whether srd_decoder_load() works. + * If it returns != SRD_OK (or segfaults) this test will fail. + */ +START_TEST(test_load) +{ + int ret; + + srd_init(DECODERS_DIR); + ret = srd_decoder_load("uart"); + fail_unless(ret == SRD_OK, "srd_decoder_load(uart) failed: %d.", ret); + ret = srd_decoder_load("spi"); + fail_unless(ret == SRD_OK, "srd_decoder_load(spi) failed: %d.", ret); + ret = srd_decoder_load("usb_signalling"); + fail_unless(ret == SRD_OK, "srd_decoder_load(usb_signalling) failed: %d.", ret); + srd_exit(); +} +END_TEST + +/* + * Check whether srd_decoder_load() fails for non-existing or bogus PDs. + * If it returns SRD_OK (or segfaults) this test will fail. + */ +START_TEST(test_load_bogus) +{ + srd_init(DECODERS_DIR); + /* http://sigrok.org/bugzilla/show_bug.cgi?id=176 */ + fail_unless(srd_decoder_load(NULL) != SRD_OK); + fail_unless(srd_decoder_load("") != SRD_OK); + fail_unless(srd_decoder_load(" ") != SRD_OK); + fail_unless(srd_decoder_load("nonexisting") != SRD_OK); + fail_unless(srd_decoder_load("UART") != SRD_OK); + fail_unless(srd_decoder_load("UaRt") != SRD_OK); + fail_unless(srd_decoder_load("u a r t") != SRD_OK); + fail_unless(srd_decoder_load("uart ") != SRD_OK); + fail_unless(srd_decoder_load(" uart") != SRD_OK); + fail_unless(srd_decoder_load(" uart ") != SRD_OK); + fail_unless(srd_decoder_load("uart spi") != SRD_OK); + srd_exit(); +} +END_TEST + +/* + * Check whether srd_decoder_load() works/fails for valid/bogus PDs. + * If it returns incorrect values (or segfaults) this test will fail. + */ +START_TEST(test_load_valid_and_bogus) +{ + srd_init(DECODERS_DIR); + fail_unless(srd_decoder_load("") != SRD_OK); + fail_unless(srd_decoder_load("uart") == SRD_OK); + fail_unless(srd_decoder_load("") != SRD_OK); + fail_unless(srd_decoder_load("spi") == SRD_OK); + fail_unless(srd_decoder_load("") != SRD_OK); + fail_unless(srd_decoder_load("can") == SRD_OK); + fail_unless(srd_decoder_load("") != SRD_OK); + srd_exit(); +} +END_TEST + +/* + * Check whether srd_decoder_load() fails when run multiple times. + * If it returns a value != SRD_OK (or segfaults) this test will fail. + * See also: http://sigrok.org/bugzilla/show_bug.cgi?id=177 + */ +START_TEST(test_load_multiple) +{ + int ret; + + srd_init(DECODERS_DIR); + ret = srd_decoder_load("uart"); + fail_unless(ret == SRD_OK, "Loading uart PD 1x failed: %d", ret); + ret = srd_decoder_load("uart"); + fail_unless(ret == SRD_OK, "Loading uart PD 2x failed: %d", ret); + ret = srd_decoder_load("uart"); + fail_unless(ret == SRD_OK, "Loading uart PD 3x failed: %d", ret); + srd_exit(); +} +END_TEST + +/* + * Check whether srd_decoder_load() fails if a non-existing PD dir is used. + * If it returns SRD_OK (or segfaults) this test will fail. + */ +START_TEST(test_load_nonexisting_pd_dir) +{ +#if 0 + /* TODO: Build libsigrokdecode with no default PD dir. */ + srd_init("/nonexisting_dir"); + fail_unless(srd_decoder_load("spi") != SRD_OK); + fail_unless(g_slist_length((GSList *)srd_decoder_list()) == 0); + srd_exit(); +#endif +} +END_TEST + +/* + * Check whether srd_decoder_list() returns a non-empty list. + * If it returns an empty list (or segfaults) this test will fail. + */ +START_TEST(test_decoder_list) +{ + srd_init(DECODERS_DIR); + srd_decoder_load_all(); + fail_unless(srd_decoder_list() != NULL); + srd_exit(); +} +END_TEST + +/* + * Check whether srd_decoder_list() without prior srd_decoder_load_all() + * returns an empty list (return value != NULL). + * If it returns a non-empty list (or segfaults) this test will fail. + */ +START_TEST(test_decoder_list_no_load) +{ + srd_init(DECODERS_DIR); + fail_unless(srd_decoder_list() == NULL); + srd_exit(); +} +END_TEST + +/* + * Check whether srd_decoder_list() without prior srd_init() + * returns an empty list. + * If it returns a non-empty list (or segfaults) this test will fail. + * See also: http://sigrok.org/bugzilla/show_bug.cgi?id=178 + */ +START_TEST(test_decoder_list_no_init) +{ + srd_decoder_load_all(); + fail_unless(srd_decoder_list() == NULL); +} +END_TEST + +/* + * Check whether srd_decoder_list() without prior srd_init() and without + * prior srd_decoder_load_all() returns an empty list. + * If it returns a non-empty list (or segfaults) this test will fail. + */ +START_TEST(test_decoder_list_no_init_no_load) +{ + fail_unless(srd_decoder_list() == NULL); +} +END_TEST + +/* + * Check whether srd_decoder_list() returns the correct number of PDs. + * If it returns a wrong number (or segfaults) this test will fail. + */ +START_TEST(test_decoder_list_correct_numbers) +{ + srd_init(DECODERS_DIR); + fail_unless(g_slist_length((GSList *)srd_decoder_list()) == 0); + srd_decoder_load("spi"); + fail_unless(g_slist_length((GSList *)srd_decoder_list()) == 1); + srd_decoder_load("uart"); + fail_unless(g_slist_length((GSList *)srd_decoder_list()) == 2); + srd_decoder_load("can"); + fail_unless(g_slist_length((GSList *)srd_decoder_list()) == 3); + srd_decoder_load("can"); /* Load same PD twice. */ + fail_unless(g_slist_length((GSList *)srd_decoder_list()) == 3); + srd_exit(); +} +END_TEST + +/* + * Check whether srd_decoder_get_by_id() works. + * If it returns NULL for valid PDs (or segfaults) this test will fail. + */ +START_TEST(test_get_by_id) +{ + srd_init(DECODERS_DIR); + srd_decoder_load("uart"); + fail_unless(srd_decoder_get_by_id("uart") != NULL); + fail_unless(srd_decoder_get_by_id("can") == NULL); + srd_decoder_load("can"); + fail_unless(srd_decoder_get_by_id("uart") != NULL); + fail_unless(srd_decoder_get_by_id("can") != NULL); + srd_exit(); +} +END_TEST + +/* + * Check whether srd_decoder_get_by_id() works multiple times in a row. + * If it returns NULL for valid PDs (or segfaults) this test will fail. + */ +START_TEST(test_get_by_id_multiple) +{ + srd_init(DECODERS_DIR); + srd_decoder_load("uart"); + fail_unless(srd_decoder_get_by_id("uart") != NULL); + fail_unless(srd_decoder_get_by_id("uart") != NULL); + fail_unless(srd_decoder_get_by_id("uart") != NULL); + fail_unless(srd_decoder_get_by_id("uart") != NULL); + srd_exit(); +} +END_TEST + +/* + * Check whether srd_decoder_get_by_id() fails for bogus PDs. + * If it returns a value != NULL (or segfaults) this test will fail. + */ +START_TEST(test_get_by_id_bogus) +{ + srd_init(DECODERS_DIR); + fail_unless(srd_decoder_get_by_id(NULL) == NULL); + fail_unless(srd_decoder_get_by_id("") == NULL); + fail_unless(srd_decoder_get_by_id(" ") == NULL); + fail_unless(srd_decoder_get_by_id("nonexisting") == NULL); + fail_unless(srd_decoder_get_by_id("sPi") == NULL); + fail_unless(srd_decoder_get_by_id("SPI") == NULL); + fail_unless(srd_decoder_get_by_id("s p i") == NULL); + fail_unless(srd_decoder_get_by_id(" spi") == NULL); + fail_unless(srd_decoder_get_by_id("spi ") == NULL); + fail_unless(srd_decoder_get_by_id(" spi ") == NULL); + fail_unless(srd_decoder_get_by_id("spi uart") == NULL); + srd_exit(); +} +END_TEST + +/* + * Check whether srd_decoder_doc_get() works. + * If it returns NULL for valid PDs (or segfaults) this test will fail. + */ +START_TEST(test_doc_get) +{ + struct srd_decoder *dec; + + srd_init(DECODERS_DIR); + srd_decoder_load("uart"); + dec = srd_decoder_get_by_id("uart"); + fail_unless(srd_decoder_doc_get(dec) != NULL); + srd_exit(); +} +END_TEST + +/* + * Check whether srd_decoder_doc_get() fails with NULL as argument. + * If it returns a value != NULL (or segfaults) this test will fail. + * See also: http://sigrok.org/bugzilla/show_bug.cgi?id=179 + */ +START_TEST(test_doc_get_null) +{ + srd_init(DECODERS_DIR); + fail_unless(srd_decoder_doc_get(NULL) == NULL); + srd_exit(); +} +END_TEST + +Suite *suite_decoder(void) +{ + Suite *s; + TCase *tc; + + s = suite_create("decoder"); + + tc = tcase_create("load"); + tcase_set_timeout(tc, 0); + tcase_add_checked_fixture(tc, srdtest_setup, srdtest_teardown); + tcase_add_test(tc, test_load_all); + tcase_add_test(tc, test_load_all_no_init); + tcase_add_test(tc, test_load); + tcase_add_test(tc, test_load_bogus); + tcase_add_test(tc, test_load_valid_and_bogus); + tcase_add_test(tc, test_load_multiple); + tcase_add_test(tc, test_load_nonexisting_pd_dir); + suite_add_tcase(s, tc); + + tc = tcase_create("list"); + tcase_add_checked_fixture(tc, srdtest_setup, srdtest_teardown); + tcase_add_test(tc, test_decoder_list); + tcase_add_test(tc, test_decoder_list_no_load); + tcase_add_test(tc, test_decoder_list_no_init); + tcase_add_test(tc, test_decoder_list_no_init_no_load); + tcase_add_test(tc, test_decoder_list_correct_numbers); + suite_add_tcase(s, tc); + + tc = tcase_create("get_by_id"); + tcase_add_test(tc, test_get_by_id); + tcase_add_test(tc, test_get_by_id_multiple); + tcase_add_test(tc, test_get_by_id_bogus); + suite_add_tcase(s, tc); + + tc = tcase_create("doc_get"); + tcase_add_test(tc, test_doc_get); + tcase_add_test(tc, test_doc_get_null); + suite_add_tcase(s, tc); + + return s; +} diff --git a/libsigrokdecode4DSL/tests/inst.c b/libsigrokdecode4DSL/tests/inst.c new file mode 100644 index 00000000..8d30b125 --- /dev/null +++ b/libsigrokdecode4DSL/tests/inst.c @@ -0,0 +1,164 @@ +/* + * This file is part of the libsigrokdecode project. + * + * Copyright (C) 2013 Uwe Hermann + * + * 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 +#include "../libsigrokdecode.h" /* First, to avoid compiler warning. */ +#include +#include +#include "lib.h" + +/* + * Check whether srd_inst_new() works. + * If it returns NULL (or segfaults) this test will fail. + */ +START_TEST(test_inst_new) +{ + struct srd_session *sess; + struct srd_decoder_inst *inst; + + srd_init(DECODERS_DIR); + srd_decoder_load("uart"); + srd_session_new(&sess); + inst = srd_inst_new(sess, "uart", NULL); + fail_unless(inst != NULL, "srd_inst_new() failed."); + srd_exit(); +} +END_TEST + +/* + * Check whether multiple srd_inst_new() calls work. + * If any of them returns NULL (or segfaults) this test will fail. + */ +START_TEST(test_inst_new_multiple) +{ + struct srd_session *sess; + struct srd_decoder_inst *inst1, *inst2, *inst3; + + inst1 = inst2 = inst3 = NULL; + + srd_init(DECODERS_DIR); + srd_decoder_load_all(); + srd_session_new(&sess); + + /* Multiple srd_inst_new() calls must work. */ + inst1 = srd_inst_new(sess, "uart", NULL); + fail_unless(inst1 != NULL, "srd_inst_new() 1 failed."); + inst2 = srd_inst_new(sess, "spi", NULL); + fail_unless(inst2 != NULL, "srd_inst_new() 2 failed."); + inst3 = srd_inst_new(sess, "can", NULL); + fail_unless(inst3 != NULL, "srd_inst_new() 3 failed."); + + /* The returned instance pointers must not be the same. */ + fail_unless(inst1 != inst2); + fail_unless(inst1 != inst3); + fail_unless(inst2 != inst3); + + /* Each instance must have another py_inst than any of the others. */ + fail_unless(inst1->py_inst != inst2->py_inst); + fail_unless(inst1->py_inst != inst3->py_inst); + fail_unless(inst2->py_inst != inst3->py_inst); + + srd_exit(); +} +END_TEST + +/* + * Check whether srd_inst_option_set() works for an empty options hash. + * If it returns != SRD_OK (or segfaults) this test will fail. + */ +START_TEST(test_inst_option_set_empty) +{ + int ret; + struct srd_session *sess; + struct srd_decoder_inst *inst; + GHashTable *options; + + srd_init(DECODERS_DIR); + srd_decoder_load_all(); + srd_session_new(&sess); + inst = srd_inst_new(sess, "uart", NULL); + options = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, + (GDestroyNotify)g_variant_unref); + ret = srd_inst_option_set(inst, options); + fail_unless(ret == SRD_OK, "srd_inst_option_set() with empty options " + "hash failed: %d.", ret); + srd_exit(); +} +END_TEST + +/* + * Check whether srd_inst_option_set() works for bogus options. + * If it returns != SRD_OK (or segfaults) this test will fail. + */ +START_TEST(test_inst_option_set_bogus) +{ + int ret; + struct srd_session *sess; + struct srd_decoder_inst *inst; + GHashTable *options; + + srd_init(DECODERS_DIR); + srd_decoder_load_all(); + srd_session_new(&sess); + inst = srd_inst_new(sess, "uart", NULL); + + options = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, + (GDestroyNotify)g_variant_unref); + + /* NULL instance. */ + ret = srd_inst_option_set(NULL, options); + fail_unless(ret != SRD_OK, "srd_inst_option_set() with NULL " + "instance failed: %d.", ret); + + /* NULL 'options' GHashTable. */ + ret = srd_inst_option_set(inst, NULL); + fail_unless(ret != SRD_OK, "srd_inst_option_set() with NULL " + "options hash failed: %d.", ret); + + /* NULL instance and NULL 'options' GHashTable. */ + ret = srd_inst_option_set(NULL, NULL); + fail_unless(ret != SRD_OK, "srd_inst_option_set() with NULL " + "instance and NULL options hash failed: %d.", ret); + + srd_exit(); +} +END_TEST + +Suite *suite_inst(void) +{ + Suite *s; + TCase *tc; + + s = suite_create("inst"); + + tc = tcase_create("new"); + tcase_add_checked_fixture(tc, srdtest_setup, srdtest_teardown); + tcase_add_test(tc, test_inst_new); + tcase_add_test(tc, test_inst_new_multiple); + suite_add_tcase(s, tc); + + tc = tcase_create("option"); + tcase_add_checked_fixture(tc, srdtest_setup, srdtest_teardown); + tcase_add_test(tc, test_inst_option_set_empty); + tcase_add_test(tc, test_inst_option_set_bogus); + suite_add_tcase(s, tc); + + return s; +} diff --git a/DSView/pv/dialogs/messagebox.h b/libsigrokdecode4DSL/tests/lib.h similarity index 51% rename from DSView/pv/dialogs/messagebox.h rename to libsigrokdecode4DSL/tests/lib.h index ca9170b3..75380a1a 100644 --- a/DSView/pv/dialogs/messagebox.h +++ b/libsigrokdecode4DSL/tests/lib.h @@ -1,8 +1,7 @@ /* - * This file is part of the DSView project. - * DSView is based on PulseView. + * This file is part of the libsigrokdecode project. * - * Copyright (C) 2016 DreamSourceLab + * Copyright (C) 2013 Uwe Hermann * * 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 @@ -19,41 +18,15 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +#ifndef LIBSIGROKDECODE_TESTS_LIB_H +#define LIBSIGROKDECODE_TESTS_LIB_H -#ifndef DSVIEW_PV_MESSAGE_H -#define DSVIEW_PV_MESSAGE_H +void srdtest_setup(void); +void srdtest_teardown(void); -#include -#include +Suite *suite_core(void); +Suite *suite_decoder(void); +Suite *suite_inst(void); +Suite *suite_session(void); -#include "../toolbars/titlebar.h" - -namespace pv { -namespace dialogs { - -class MessageBox : public QDialog -{ - Q_OBJECT - -public: - MessageBox(QWidget *parent); - QMessageBox *mBox(); - - int exec(); - -protected: - void accept(); - void reject(); - -private slots: - void on_button(QAbstractButton* btn); - -private: - QMessageBox *_msg; - toolbars::TitleBar *_titlebar; -}; - -} // namespace dialogs -} // namespace pv - -#endif // DSVIEW_PV_MESSAGE_H +#endif diff --git a/libsigrokdecode4DSL/tests/main.c b/libsigrokdecode4DSL/tests/main.c new file mode 100644 index 00000000..19c459d6 --- /dev/null +++ b/libsigrokdecode4DSL/tests/main.c @@ -0,0 +1,57 @@ +/* + * This file is part of the libsigrokdecode project. + * + * Copyright (C) 2013 Uwe Hermann + * + * 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 +#include "../libsigrokdecode.h" /* First, to avoid compiler warning. */ +#include +#include +#include "lib.h" + +void srdtest_setup(void) +{ + /* Silence libsigrokdecode while the unit tests run. */ + srd_log_loglevel_set(SRD_LOG_NONE); +} + +void srdtest_teardown(void) +{ +} + +int main(void) +{ + int ret; + Suite *s; + SRunner *srunner; + + s = suite_create("mastersuite"); + srunner = srunner_create(s); + + /* Add all testsuites to the master suite. */ + srunner_add_suite(srunner, suite_core()); + srunner_add_suite(srunner, suite_decoder()); + srunner_add_suite(srunner, suite_inst()); + srunner_add_suite(srunner, suite_session()); + + srunner_run_all(srunner, CK_VERBOSE); + ret = srunner_ntests_failed(srunner); + srunner_free(srunner); + + return (ret == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} diff --git a/libsigrokdecode4DSL/tests/session.c b/libsigrokdecode4DSL/tests/session.c new file mode 100644 index 00000000..431ab5ec --- /dev/null +++ b/libsigrokdecode4DSL/tests/session.c @@ -0,0 +1,251 @@ +/* + * This file is part of the libsigrokdecode project. + * + * Copyright (C) 2013 Uwe Hermann + * + * 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 +#include "../libsigrokdecode-internal.h" /* First, to avoid compiler warning. */ +#include "../libsigrokdecode.h" +#include +#include +#include +#include "lib.h" + +/* + * Check whether srd_session_new() works. + * If it returns != SRD_OK (or segfaults) this test will fail. + */ +START_TEST(test_session_new) +{ + int ret; + struct srd_session *sess; + + srd_init(NULL); + ret = srd_session_new(&sess); + fail_unless(ret == SRD_OK, "srd_session_new() failed: %d.", ret); + srd_exit(); +} +END_TEST + +/* + * Check whether srd_session_new() fails for bogus parameters. + * If it returns SRD_OK (or segfaults) this test will fail. + */ +START_TEST(test_session_new_bogus) +{ + int ret; + + srd_init(NULL); + ret = srd_session_new(NULL); + fail_unless(ret != SRD_OK, "srd_session_new(NULL) worked."); + srd_exit(); +} +END_TEST + +/* + * Check whether multiple srd_session_new() calls work. + * If any call returns != SRD_OK (or segfaults) this test will fail. + */ +START_TEST(test_session_new_multiple) +{ + int ret; + struct srd_session *sess1, *sess2, *sess3; + + sess1 = sess2 = sess3 = NULL; + + srd_init(NULL); + + /* Multiple srd_session_new() calls must work. */ + ret = srd_session_new(&sess1); + fail_unless(ret == SRD_OK, "srd_session_new() 1 failed: %d.", ret); + ret = srd_session_new(&sess2); + fail_unless(ret == SRD_OK, "srd_session_new() 2 failed: %d.", ret); + ret = srd_session_new(&sess3); + fail_unless(ret == SRD_OK, "srd_session_new() 3 failed: %d.", ret); + + /* The returned session pointers must all be non-NULL. */ + fail_unless(sess1 != NULL); + fail_unless(sess2 != NULL); + fail_unless(sess3 != NULL); + + /* The returned session pointers must not be the same. */ + fail_unless(sess1 != sess2); + fail_unless(sess1 != sess3); + fail_unless(sess2 != sess3); + + /* Each session must have another ID than any other session. */ + fail_unless(sess1->session_id != sess2->session_id); + fail_unless(sess1->session_id != sess3->session_id); + fail_unless(sess2->session_id != sess3->session_id); + + /* Destroying any of the sessions must work. */ + ret = srd_session_destroy(sess1); + fail_unless(ret == SRD_OK, "srd_session_destroy() 1 failed: %d.", ret); + ret = srd_session_destroy(sess2); + fail_unless(ret == SRD_OK, "srd_session_destroy() 2 failed: %d.", ret); + ret = srd_session_destroy(sess3); + fail_unless(ret == SRD_OK, "srd_session_destroy() 3 failed: %d.", ret); + + srd_exit(); +} +END_TEST + +/* + * Check whether srd_session_destroy() works. + * If it returns != SRD_OK (or segfaults) this test will fail. + */ +START_TEST(test_session_destroy) +{ + int ret; + struct srd_session *sess; + + srd_init(NULL); + srd_session_new(&sess); + ret = srd_session_destroy(sess); + fail_unless(ret == SRD_OK, "srd_session_destroy() failed: %d.", ret); + srd_exit(); +} +END_TEST + +/* + * Check whether srd_session_destroy() fails for bogus sessions. + * If it returns SRD_OK (or segfaults) this test will fail. + */ +START_TEST(test_session_destroy_bogus) +{ + int ret; + + srd_init(NULL); + ret = srd_session_destroy(NULL); + fail_unless(ret != SRD_OK, "srd_session_destroy() failed: %d.", ret); + srd_exit(); +} +END_TEST + +static void conf_check_ok(struct srd_session *sess, int key, uint64_t x) +{ + int ret; + + ret = srd_session_metadata_set(sess, key, g_variant_new_uint64(x)); + fail_unless(ret == SRD_OK, "srd_session_metadata_set(%p, %d, %" + PRIu64 ") failed: %d.", sess, key, x, ret); +} + +static void conf_check_fail(struct srd_session *sess, int key, uint64_t x) +{ + int ret; + + ret = srd_session_metadata_set(sess, key, g_variant_new_uint64(x)); + fail_unless(ret != SRD_OK, "srd_session_metadata_set(%p, %d, %" + PRIu64 ") worked.", sess, key, x); +} + +static void conf_check_fail_null(struct srd_session *sess, int key) +{ + int ret; + + ret = srd_session_metadata_set(sess, key, NULL); + fail_unless(ret != SRD_OK, + "srd_session_metadata_set(NULL) for key %d worked.", key); +} + +static void conf_check_fail_str(struct srd_session *sess, int key, const char *s) +{ + int ret; + + ret = srd_session_metadata_set(sess, key, g_variant_new_string(s)); + fail_unless(ret != SRD_OK, "srd_session_metadata_set() for key %d " + "failed: %d.", key, ret); +} + +/* + * Check whether srd_session_metadata_set() works. + * If it returns != SRD_OK (or segfaults) this test will fail. + */ +START_TEST(test_session_metadata_set) +{ + uint64_t i; + struct srd_session *sess; + + srd_init(NULL); + srd_session_new(&sess); + /* Try a bunch of values. */ + for (i = 0; i < 1000; i++) + conf_check_ok(sess, SRD_CONF_SAMPLERATE, i); + /* Try the max. possible value. */ + conf_check_ok(sess, SRD_CONF_SAMPLERATE, UINT64_MAX); + srd_session_destroy(sess); + srd_exit(); +} +END_TEST + +/* + * Check whether srd_session_metadata_set() fails with invalid input. + * If it returns SRD_OK (or segfaults) this test will fail. + */ +START_TEST(test_session_metadata_set_bogus) +{ + struct srd_session *sess; + + srd_init(NULL); + srd_session_new(&sess); + + /* Incorrect GVariant type (currently only uint64 is used). */ + conf_check_fail_str(sess, SRD_CONF_SAMPLERATE, ""); + conf_check_fail_str(sess, SRD_CONF_SAMPLERATE, "Foo"); + + /* NULL data pointer. */ + conf_check_fail_null(sess, SRD_CONF_SAMPLERATE); + + /* NULL session. */ + conf_check_fail(NULL, SRD_CONF_SAMPLERATE, 0); + + /* Invalid keys. */ + conf_check_fail(sess, -1, 0); + conf_check_fail(sess, 9, 0); + conf_check_fail(sess, 123, 0); + + srd_session_destroy(sess); + srd_exit(); +} +END_TEST + +Suite *suite_session(void) +{ + Suite *s; + TCase *tc; + + s = suite_create("session"); + + tc = tcase_create("new_destroy"); + tcase_add_checked_fixture(tc, srdtest_setup, srdtest_teardown); + tcase_add_test(tc, test_session_new); + tcase_add_test(tc, test_session_new_bogus); + tcase_add_test(tc, test_session_new_multiple); + tcase_add_test(tc, test_session_destroy); + tcase_add_test(tc, test_session_destroy_bogus); + suite_add_tcase(s, tc); + + tc = tcase_create("config"); + tcase_add_checked_fixture(tc, srdtest_setup, srdtest_teardown); + tcase_add_test(tc, test_session_metadata_set); + tcase_add_test(tc, test_session_metadata_set_bogus); + suite_add_tcase(s, tc); + + return s; +} diff --git a/libsigrokdecode4DSL/tools/install-decoders b/libsigrokdecode4DSL/tools/install-decoders new file mode 100755 index 00000000..465c70e4 --- /dev/null +++ b/libsigrokdecode4DSL/tools/install-decoders @@ -0,0 +1,112 @@ +#!/usr/bin/env python3 +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2013 Bert Vermeulen +## +## 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 3 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, see . +## + +import os +import sys +from shutil import copy +from getopt import getopt + + +def install(srcdir, dstdir): + worklist = [] + for pd in os.listdir(srcdir): + pd_dir = srcdir + '/' + pd + if not os.path.isdir(pd_dir): + continue + install_list = [] + for f in os.listdir(pd_dir): + pd_file = pd_dir + '/' + f + if not os.path.isfile(pd_file): + continue + if f == 'config': + install_list.extend(config_get_extra_install(pd_file)) + elif f[-3:] == '.py': + install_list.append(f) + worklist.append((pd, pd_dir, install_list)) + + print("Installing %d protocol decoders:" % len(worklist)) + col = 0 + for pd, pd_dir, install_list in worklist: + msg = pd + ' ' + if (col + len(msg) > 80): + print() + col = 0 + print(msg, end='') + col += len(msg) + pd_dst = os.path.join(dstdir, pd) + try: + os.mkdir(pd_dst) + except OSError as e: + if e.errno != os.errno.EEXIST: + raise + else: + pass + for f in install_list: + copy(os.path.join(pd_dir, f), pd_dst) + print() + + +def config_get_extra_install(config_file): + install_list = [] + for line in open(config_file).read().split('\n'): + line = line.strip() + if len(line) == 0 or line[0] == '#': + continue + words = line.split() + if words[0] != 'extra-install': + continue + install_list.extend(words[1:]) + + return install_list + + +def usage(msg=None): + if msg: + print(msg) + ret = 1 + else: + ret = 0 + print("""Usage: + install-decoders [-i ] -o """) + sys.exit(ret) + + +# +# main +# + +src = 'decoders' +dst = None +try: + opts, args = getopt(sys.argv[1:], 'i:o:') + for opt, arg in opts: + if opt == '-i': + src = arg + elif opt == '-o': + dst = arg +except Exception as e: + usage(str(e)) + +if len(args) != 0 or dst is None: + usage() + +install(src, dst) + + diff --git a/libsigrokdecode4DSL/type_decoder.c b/libsigrokdecode4DSL/type_decoder.c new file mode 100644 index 00000000..b922d8a9 --- /dev/null +++ b/libsigrokdecode4DSL/type_decoder.c @@ -0,0 +1,412 @@ +/* + * This file is part of the libsigrokdecode project. + * + * Copyright (C) 2012 Bert Vermeulen + * Copyright (C) 2016 DreamSourceLab + * + * 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 3 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, see . + */ + +#include "config.h" +#include "libsigrokdecode-internal.h" /* First, so we avoid a _POSIX_C_SOURCE warning. */ +#include "libsigrokdecode.h" +#include + +typedef struct { + PyObject_HEAD +} srd_Decoder; + +/* This is only used for nicer srd_dbg() output. */ +static const char *OUTPUT_TYPES[] = { + "OUTPUT_ANN", + "OUTPUT_PYTHON", + "OUTPUT_BINARY", + "OUTPUT_META", +}; + +static int convert_annotation(struct srd_decoder_inst *di, PyObject *obj, + struct srd_proto_data *pdata) +{ + PyObject *py_tmp; + struct srd_proto_data_annotation *pda; + unsigned int ann_class; + char **ann_text; + gpointer ann_type_ptr; + + /* Should be a list of [annotation class, [string, ...]]. */ + if (!PyList_Check(obj) && !PyTuple_Check(obj)) { + srd_err("Protocol decoder %s submitted %s instead of list.", + di->decoder->name, obj->ob_type->tp_name); + return SRD_ERR_PYTHON; + } + + /* Should have 2 elements. */ + if (PyList_Size(obj) != 2) { + srd_err("Protocol decoder %s submitted annotation list with " + "%zd elements instead of 2", di->decoder->name, + PyList_Size(obj)); + return SRD_ERR_PYTHON; + } + + /* + * The first element should be an integer matching a previously + * registered annotation class. + */ + py_tmp = PyList_GetItem(obj, 0); + if (!PyLong_Check(py_tmp)) { + srd_err("Protocol decoder %s submitted annotation list, but " + "first element was not an integer.", di->decoder->name); + return SRD_ERR_PYTHON; + } + ann_class = PyLong_AsLong(py_tmp); +// if (!(pdo = g_slist_nth_data(di->decoder->annotations, ann_class))) { +// srd_err("Protocol decoder %s submitted data to unregistered " +// "annotation class %d.", di->decoder->name, ann_class); +// return SRD_ERR_PYTHON; +// } + if (ann_class >= g_slist_length(di->decoder->ann_types)) { + srd_err("Protocol decoder %s submitted data to unregistered " + "annotation class %d.", di->decoder->name, ann_class); + return SRD_ERR_PYTHON; + } + ann_type_ptr = g_slist_nth_data(di->decoder->ann_types, ann_class); + + /* Second element must be a list. */ + py_tmp = PyList_GetItem(obj, 1); + if (!PyList_Check(py_tmp)) { + srd_err("Protocol decoder %s submitted annotation list, but " + "second element was not a list.", di->decoder->name); + return SRD_ERR_PYTHON; + } + if (py_strseq_to_char(py_tmp, &ann_text) != SRD_OK) { + srd_err("Protocol decoder %s submitted annotation list, but " + "second element was malformed.", di->decoder->name); + return SRD_ERR_PYTHON; + } + + pda = g_malloc(sizeof(struct srd_proto_data_annotation)); + pda->ann_class = ann_class; + pda->ann_type = GPOINTER_TO_INT(ann_type_ptr); + pda->ann_text = ann_text; + pdata->data = pda; + + //Py_DECREF(py_tmp); + + return SRD_OK; +} + +static int convert_binary(struct srd_decoder_inst *di, PyObject *obj, + struct srd_proto_data *pdata) +{ + struct srd_proto_data_binary *pdb; + PyObject *py_tmp; + Py_ssize_t size; + int bin_class; + char *class_name, *buf; + + /* Should be a tuple of (binary class, bytes). */ + if (!PyTuple_Check(obj)) { + srd_err("Protocol decoder %s submitted SRD_OUTPUT_BINARY with " + "%s instead of tuple.", di->decoder->name, + obj->ob_type->tp_name); + return SRD_ERR_PYTHON; + } + + /* Should have 2 elements. */ + if (PyTuple_Size(obj) != 2) { + srd_err("Protocol decoder %s submitted SRD_OUTPUT_BINARY tuple " + "with %zd elements instead of 2", di->decoder->name, + PyList_Size(obj)); + return SRD_ERR_PYTHON; + } + + /* The first element should be an integer. */ + py_tmp = PyTuple_GetItem(obj, 0); + if (!PyLong_Check(py_tmp)) { + srd_err("Protocol decoder %s submitted SRD_OUTPUT_BINARY tuple, " + "but first element was not an integer.", di->decoder->name); + return SRD_ERR_PYTHON; + } + bin_class = PyLong_AsLong(py_tmp); + if (!(class_name = g_slist_nth_data(di->decoder->binary, bin_class))) { + srd_err("Protocol decoder %s submitted SRD_OUTPUT_BINARY with " + "unregistered binary class %d.", di->decoder->name, bin_class); + return SRD_ERR_PYTHON; + } + + /* Second element should be bytes. */ + py_tmp = PyTuple_GetItem(obj, 1); + if (!PyBytes_Check(py_tmp)) { + srd_err("Protocol decoder %s submitted SRD_OUTPUT_BINARY tuple, " + "but second element was not bytes.", di->decoder->name); + return SRD_ERR_PYTHON; + } + + /* Consider an empty set of bytes a bug. */ + if (PyBytes_Size(py_tmp) == 0) { + srd_err("Protocol decoder %s submitted SRD_OUTPUT_BINARY " + "with empty data set.", di->decoder->name); + return SRD_ERR_PYTHON; + } + + pdb = g_malloc(sizeof(struct srd_proto_data_binary)); + if (PyBytes_AsStringAndSize(py_tmp, &buf, &size) == -1) + return SRD_ERR_PYTHON; + pdb->bin_class = bin_class; + pdb->size = size; + if (!(pdb->data = g_try_malloc(pdb->size))) + return SRD_ERR_MALLOC; + memcpy((void *)pdb->data, (const void *)buf, pdb->size); + pdata->data = pdb; + + //Py_DECREF(py_tmp); + + return SRD_OK; +} + +static int convert_meta(struct srd_proto_data *pdata, PyObject *obj) +{ + long long intvalue; + double dvalue; + + if (pdata->pdo->meta_type == G_VARIANT_TYPE_INT64) { + if (!PyLong_Check(obj)) { + PyErr_Format(PyExc_TypeError, "This output was registered " + "as 'int', but '%s' was passed.", obj->ob_type->tp_name); + return SRD_ERR_PYTHON; + } + intvalue = PyLong_AsLongLong(obj); + if (PyErr_Occurred()) + return SRD_ERR_PYTHON; + pdata->data = g_variant_new_int64(intvalue); + } else if (pdata->pdo->meta_type == G_VARIANT_TYPE_DOUBLE) { + if (!PyFloat_Check(obj)) { + PyErr_Format(PyExc_TypeError, "This output was registered " + "as 'float', but '%s' was passed.", obj->ob_type->tp_name); + return SRD_ERR_PYTHON; + } + dvalue = PyFloat_AsDouble(obj); + if (PyErr_Occurred()) + return SRD_ERR_PYTHON; + pdata->data = g_variant_new_double(dvalue); + } + + return SRD_OK; +} + +static PyObject *Decoder_put(PyObject *self, PyObject *args) +{ + GSList *l; + PyObject *py_data, *py_res; + struct srd_decoder_inst *di, *next_di; + struct srd_pd_output *pdo; + struct srd_proto_data *pdata; + uint64_t start_sample, end_sample; + int output_id; + struct srd_pd_callback *cb; + struct srd_proto_data_binary *pdb; + struct srd_proto_data_annotation *pda; + char **annotations; + + if (!(di = srd_inst_find_by_obj(NULL, self))) { + /* Shouldn't happen. */ + srd_dbg("put(): self instance not found."); + return NULL; + } + + if (!PyArg_ParseTuple(args, "KKiO", &start_sample, &end_sample, + &output_id, &py_data)) { + /* + * This throws an exception, but by returning NULL here we let + * Python raise it. This results in a much better trace in + * controller.c on the decode() method call. + */ + return NULL; + } + + if (!(l = g_slist_nth(di->pd_output, output_id))) { + srd_err("Protocol decoder %s submitted invalid output ID %d.", + di->decoder->name, output_id); + return NULL; + } + pdo = l->data; + + srd_spew("Instance %s put %" PRIu64 "-%" PRIu64 " %s on oid %d.", + di->inst_id, start_sample, end_sample, + OUTPUT_TYPES[pdo->output_type], output_id); + + pdata = g_malloc0(sizeof(struct srd_proto_data)); + pdata->start_sample = start_sample; + pdata->end_sample = end_sample; + pdata->pdo = pdo; + + switch (pdo->output_type) { + case SRD_OUTPUT_ANN: + /* Annotations are only fed to callbacks. */ + if ((cb = srd_pd_output_callback_find(di->sess, pdo->output_type))) { + /* Convert from PyDict to srd_proto_data_annotation. */ + if (convert_annotation(di, py_data, pdata) != SRD_OK) { + /* An error was already logged. */ + break; + } + cb->cb(pdata, cb->cb_data); + pda = pdata->data; + annotations = (char**)pda->ann_text; + while(*annotations) { + g_free(*annotations); + annotations++; + } + g_free(pda->ann_text); + g_free(pda); + } + break; + case SRD_OUTPUT_PYTHON: + for (l = di->next_di; l; l = l->next) { + next_di = l->data; + srd_spew("Sending %" PRIu64 "-%" PRIu64 " to instance %s", + start_sample, end_sample, next_di->inst_id); + if (!(py_res = PyObject_CallMethod( + next_di->py_inst, "decode", "KKO", start_sample, + end_sample, py_data))) { + srd_exception_catch("Calling %s decode(): ", NULL, + next_di->inst_id); + } + Py_XDECREF(py_res); + } + if ((cb = srd_pd_output_callback_find(di->sess, pdo->output_type))) { + /* Frontends aren't really supposed to get Python + * callbacks, but it's useful for testing. */ + pdata->data = py_data; + cb->cb(pdata, cb->cb_data); + } + break; + case SRD_OUTPUT_BINARY: + if ((cb = srd_pd_output_callback_find(di->sess, pdo->output_type))) { + /* Convert from PyDict to srd_proto_data_binary. */ + if (convert_binary(di, py_data, pdata) != SRD_OK) { + /* An error was already logged. */ + break; + } + cb->cb(pdata, cb->cb_data); + pdb = pdata->data; + g_free(pdb->data); + g_free(pdb); + } + break; + case SRD_OUTPUT_META: + if ((cb = srd_pd_output_callback_find(di->sess, pdo->output_type))) { + /* Annotations need converting from PyObject. */ + if (convert_meta(pdata, py_data) != SRD_OK) { + /* An exception was already set up. */ + break; + } + cb->cb(pdata, cb->cb_data); + } + break; + default: + srd_err("Protocol decoder %s submitted invalid output type %d.", + di->decoder->name, pdo->output_type); + break; + } + + g_free(pdata); + //Py_XDECREF(py_data); + + Py_RETURN_NONE; +} + +static PyObject *Decoder_register(PyObject *self, PyObject *args, + PyObject *kwargs) +{ + struct srd_decoder_inst *di; + struct srd_pd_output *pdo; + PyObject *py_new_output_id; + PyTypeObject *meta_type_py; + const GVariantType *meta_type_gv; + int output_type; + char *proto_id, *meta_name, *meta_descr; + char *keywords[] = {"output_type", "proto_id", "meta", NULL}; + + meta_type_py = NULL; + meta_type_gv = NULL; + meta_name = meta_descr = NULL; + + if (!(di = srd_inst_find_by_obj(NULL, self))) { + PyErr_SetString(PyExc_Exception, "decoder instance not found"); + return NULL; + } + + /* Default to instance id, which defaults to class id. */ + proto_id = di->inst_id; + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i|s(Oss)", keywords, + &output_type, &proto_id, + &meta_type_py, &meta_name, &meta_descr)) { + /* Let Python raise this exception. */ + return NULL; + } + + /* Check if the meta value's type is supported. */ + if (output_type == SRD_OUTPUT_META) { + if (meta_type_py == &PyLong_Type) + meta_type_gv = G_VARIANT_TYPE_INT64; + else if (meta_type_py == &PyFloat_Type) + meta_type_gv = G_VARIANT_TYPE_DOUBLE; + else { + PyErr_Format(PyExc_TypeError, "Unsupported type '%s'.", + meta_type_py->tp_name); + return NULL; + } + } + + srd_dbg("Instance %s creating new output type %d for %s.", + di->inst_id, output_type, proto_id); + + pdo = g_malloc(sizeof(struct srd_pd_output)); + + /* pdo_id is just a simple index, nothing is deleted from this list anyway. */ + pdo->pdo_id = g_slist_length(di->pd_output); + pdo->output_type = output_type; + pdo->di = di; + pdo->proto_id = g_strdup(proto_id); + + if (output_type == SRD_OUTPUT_META) { + pdo->meta_type = meta_type_gv; + pdo->meta_name = g_strdup(meta_name); + pdo->meta_descr = g_strdup(meta_descr); + } + + di->pd_output = g_slist_append(di->pd_output, pdo); + py_new_output_id = Py_BuildValue("i", pdo->pdo_id); + + return py_new_output_id; +} + +static PyMethodDef Decoder_methods[] = { + {"put", Decoder_put, METH_VARARGS, + "Accepts a dictionary with the following keys: startsample, endsample, data"}, + {"register", (PyCFunction)Decoder_register, METH_VARARGS|METH_KEYWORDS, + "Register a new output stream"}, + {NULL, NULL, 0, NULL} +}; + +/** @cond PRIVATE */ +SRD_PRIV PyTypeObject srd_Decoder_type = { + PyVarObject_HEAD_INIT(NULL, 0) + .tp_name = "sigrokdecode.Decoder", + .tp_basicsize = sizeof(srd_Decoder), + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + .tp_doc = "sigrok Decoder base class", + .tp_methods = Decoder_methods, +}; +/** @endcond */ diff --git a/libsigrokdecode4DSL/type_logic.c b/libsigrokdecode4DSL/type_logic.c new file mode 100644 index 00000000..5724d738 --- /dev/null +++ b/libsigrokdecode4DSL/type_logic.c @@ -0,0 +1,116 @@ +/* + * This file is part of the libsigrokdecode project. + * + * Copyright (C) 2012 Bert Vermeulen + * Copyright (C) 2016 DreamSourceLab + * + * 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 3 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, see . + */ + +#include "config.h" +#include "libsigrokdecode-internal.h" /* First, so we avoid a _POSIX_C_SOURCE warning. */ +#include "libsigrokdecode.h" +#include +#include + +static PyObject *srd_logic_iter(PyObject *self) +{ + return self; +} + +static PyObject *srd_logic_iternext(PyObject *self) +{ + srd_logic *logic; + PyObject *py_samplenum, *py_samples; + uint8_t *sample_pos, sample; + int byte_offset, bit_offset, i; + + logic = (srd_logic *)self; + uint64_t offset = floor(logic->itercnt); + logic->di->cur_pos = logic->cur_pos; + logic->di->logic_mask = 0; + logic->di->exp_logic = 0; + if (logic->logic_mask != 0) { + if (logic->edge_index == -1) + logic->di->edge_index = -1; + else + logic->di->edge_index = logic->di->dec_channelmap[logic->edge_index]; + for (i = 0; i < logic->di->dec_num_channels; i++) { + int index = logic->di->dec_channelmap[i]; + if ((logic->logic_mask & (0x01 << i)) && index != -1) { + logic->di->logic_mask += 0x01 << index; + logic->di->exp_logic += ((logic->exp_logic & (0x01 << i)) >> i) << index; + } + } + } + if (offset >= logic->inbuflen / logic->di->data_unitsize || logic->logic_mask != 0) { + /* End iteration loop. */ + return NULL; + } + + /* + * Convert the bit-packed sample to an array of bytes, with only 0x01 + * and 0x00 values, so the PD doesn't need to do any bitshifting. + */ + sample_pos = logic->inbuf + offset * logic->di->data_unitsize; + for (i = 0; i < logic->di->dec_num_channels; i++) { + /* A channelmap value of -1 means "unused optional channel". */ + if (logic->di->dec_channelmap[i] == -1) { + /* Value of unused channel is 0xff, instead of 0 or 1. */ + logic->di->channel_samples[i] = 0xff; + } else { + byte_offset = logic->di->dec_channelmap[i] / 8; + bit_offset = logic->di->dec_channelmap[i] % 8; + sample = *(sample_pos + byte_offset) & (1 << bit_offset) ? 1 : 0; + logic->di->channel_samples[i] = sample; + } + } + + /* Prepare the next samplenum/sample list in this iteration. */ + py_samplenum = PyLong_FromUnsignedLongLong(logic->start_samplenum + offset); + PyList_SetItem(logic->sample, 0, py_samplenum); + py_samples = PyBytes_FromStringAndSize((const char *)logic->di->channel_samples, + logic->di->dec_num_channels); + PyList_SetItem(logic->sample, 1, py_samples); + Py_INCREF(logic->sample); + + return logic->sample; +} + +static PyMemberDef srd_logic_members[] = { + {"itercnt", T_FLOAT, offsetof(srd_logic, itercnt), 0, + "next expacted samples offset"}, + {"logic_mask", T_ULONGLONG, offsetof(srd_logic, logic_mask), 0, + "next expacted logic value mask"}, + {"exp_logic", T_ULONGLONG, offsetof(srd_logic, exp_logic), 0, + "next expacted logic value"}, + {"edge_index", T_INT, offsetof(srd_logic, edge_index), 0, + "channel index of next expacted edge"}, + {"cur_pos", T_ULONGLONG, offsetof(srd_logic, cur_pos), 0, + "current sample position"}, + {NULL} /* Sentinel */ +}; + +/** @cond PRIVATE */ +SRD_PRIV PyTypeObject srd_logic_type = { + PyVarObject_HEAD_INIT(NULL, 0) + .tp_name = "srd_logic", + .tp_basicsize = sizeof(srd_logic), + .tp_flags = Py_TPFLAGS_DEFAULT, + .tp_doc = "Sigrokdecode logic sample object", + .tp_members = srd_logic_members, + .tp_iter = srd_logic_iter, + .tp_iternext = srd_logic_iternext, +}; +/** @endcond */ diff --git a/libsigrokdecode4DSL/util.c b/libsigrokdecode4DSL/util.c new file mode 100644 index 00000000..93e953eb --- /dev/null +++ b/libsigrokdecode4DSL/util.c @@ -0,0 +1,204 @@ +/* + * This file is part of the libsigrokdecode project. + * + * Copyright (C) 2010 Uwe Hermann + * Copyright (C) 2012 Bert Vermeulen + * + * 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 3 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, see . + */ + +#include "config.h" +#include "libsigrokdecode-internal.h" /* First, so we avoid a _POSIX_C_SOURCE warning. */ +#include "libsigrokdecode.h" + +/** + * Get the value of a Python object's attribute, returned as a newly + * allocated char *. + * + * @param py_obj The object to probe. + * @param attr Name of the attribute to retrieve. + * @param outstr ptr to char * storage to be filled in. + * + * @return SRD_OK upon success, a (negative) error code otherwise. + * The 'outstr' argument points to a malloc()ed string upon success. + * + * @private + */ +SRD_PRIV int py_attr_as_str(const PyObject *py_obj, const char *attr, + char **outstr) +{ + PyObject *py_str; + int ret; + + if (!PyObject_HasAttrString((PyObject *)py_obj, attr)) { + srd_dbg("%s object has no attribute '%s'.", + Py_TYPE(py_obj)->tp_name, attr); + return SRD_ERR_PYTHON; + } + + if (!(py_str = PyObject_GetAttrString((PyObject *)py_obj, attr))) { + srd_exception_catch("", NULL); + return SRD_ERR_PYTHON; + } + + if (!PyUnicode_Check(py_str)) { + srd_dbg("%s attribute should be a string, but is a %s.", + attr, Py_TYPE(py_str)->tp_name); + Py_DecRef(py_str); + return SRD_ERR_PYTHON; + } + + ret = py_str_as_str(py_str, outstr); + Py_DecRef(py_str); + + return ret; +} + +/** + * Get the value of a Python dictionary item, returned as a newly + * allocated char *. + * + * @param py_obj The dictionary to probe. + * @param key Key of the item to retrieve. + * @param outstr Pointer to char * storage to be filled in. + * + * @return SRD_OK upon success, a (negative) error code otherwise. + * The 'outstr' argument points to a malloc()ed string upon success. + * + * @private + */ +SRD_PRIV int py_dictitem_as_str(const PyObject *py_obj, const char *key, + char **outstr) +{ + PyObject *py_value; + int ret; + + if (!PyDict_Check((PyObject *)py_obj)) { + srd_dbg("Object is a %s, not a dictionary.", + Py_TYPE((PyObject *)py_obj)->tp_name); + return SRD_ERR_PYTHON; + } + + if (!(py_value = PyDict_GetItemString((PyObject *)py_obj, key))) { + srd_dbg("Dictionary has no attribute '%s'.", key); + return SRD_ERR_PYTHON; + } + + if (!PyUnicode_Check(py_value)) { + srd_dbg("Dictionary value for %s should be a string, but is " + "a %s.", key, Py_TYPE(py_value)->tp_name); + return SRD_ERR_PYTHON; + } + + ret = py_str_as_str(py_value, outstr); + + return ret; +} + +/** + * Get the value of a Python unicode string object, returned as a newly + * allocated char *. + * + * @param py_str The unicode string object. + * @param outstr ptr to char * storage to be filled in. + * + * @return SRD_OK upon success, a (negative) error code otherwise. + * The 'outstr' argument points to a malloc()ed string upon success. + * + * @private + */ +SRD_PRIV int py_str_as_str(const PyObject *py_str, char **outstr) +{ + PyObject *py_encstr; + int ret; + char *str; + + py_encstr = NULL; + str = NULL; + ret = SRD_OK; + + if (!PyUnicode_Check((PyObject *)py_str)) { + srd_dbg("Object is a %s, not a string object.", + Py_TYPE((PyObject *)py_str)->tp_name); + ret = SRD_ERR_PYTHON; + goto err_out; + } + + if (!(py_encstr = PyUnicode_AsEncodedString((PyObject *)py_str, + "utf-8", NULL))) { + ret = SRD_ERR_PYTHON; + goto err_out; + } + if (!(str = PyBytes_AS_STRING(py_encstr))) { + ret = SRD_ERR_PYTHON; + goto err_out; + } + + *outstr = g_strdup(str); + +err_out: + if (py_encstr) + Py_XDECREF(py_encstr); + + if (PyErr_Occurred()) { + srd_exception_catch("string conversion failed", NULL); + } + + return ret; +} + +/** + * Convert a Python list of unicode strings to a NULL-terminated UTF8-encoded + * char * array. The caller must g_free() each string when finished. + * + * @param py_strlist The list object. + * @param outstr ptr to char ** storage to be filled in. + * + * @return SRD_OK upon success, a (negative) error code otherwise. + * The 'outstr' argument points to a g_malloc()ed char** upon success. + * + * @private + */ +SRD_PRIV int py_strseq_to_char(const PyObject *py_strseq, char ***outstr) +{ + PyObject *py_seq, *py_str; + int list_len, i; + char **out, *str; + + list_len = PySequence_Size((PyObject *)py_strseq); + if (!(out = g_try_malloc(sizeof(char *) * (list_len + 1)))) { + srd_err("Failed to g_malloc() 'out'."); + return SRD_ERR_MALLOC; + } + for (i = 0; i < list_len; i++) { +// if (!(py_str = PyUnicode_AsEncodedString( +// PySequence_GetItem((PyObject *)py_strseq, i), "utf-8", NULL))) +// return SRD_ERR_PYTHON; +// if (!(str = PyBytes_AS_STRING(py_str))) +// return SRD_ERR_PYTHON; + py_seq = PySequence_GetItem((PyObject *)py_strseq, i); + if (!(py_str = PyUnicode_AsEncodedString(py_seq, "utf-8", NULL))) + return SRD_ERR_PYTHON; + if (!(str = PyBytes_AS_STRING(py_str))) + return SRD_ERR_PYTHON; + + out[i] = g_strdup(str); + Py_XDECREF(py_seq); + Py_XDECREF(py_str); + } + out[i] = NULL; + *outstr = out; + + return SRD_OK; +} diff --git a/libsigrokdecode4DSL/version.c b/libsigrokdecode4DSL/version.c new file mode 100644 index 00000000..e708fdaa --- /dev/null +++ b/libsigrokdecode4DSL/version.c @@ -0,0 +1,149 @@ +/* + * This file is part of the libsigrokdecode project. + * + * Copyright (C) 2012-2013 Uwe Hermann + * + * 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 "config.h" +#include "libsigrokdecode.h" + +/** + * @file + * + * Version number querying functions, definitions, and macros. + */ + +/** + * @defgroup grp_versions Versions + * + * Version number querying functions, definitions, and macros. + * + * This set of API calls returns two different version numbers related + * to libsigrokdecode. The "package version" is the release version number + * of the libsigrokdecode tarball in the usual "major.minor.micro" format, + * e.g. "0.1.0". + * + * The "library version" is independent of that; it is the libtool version + * number in the "current:revision:age" format, e.g. "2:0:0". + * See http://www.gnu.org/software/libtool/manual/libtool.html#Libtool-versioning for details. + * + * Both version numbers (and/or individual components of them) can be + * retrieved via the API calls at runtime, and/or they can be checked at + * compile/preprocessor time using the respective macros. + * + * @{ + */ + +/** + * Get the major libsigrokdecode package version number. + * + * @return The major package version number. + * + * @since 0.1.0 + */ +SRD_API int srd_package_version_major_get(void) +{ + return SRD_PACKAGE_VERSION_MAJOR; +} + +/** + * Get the minor libsigrokdecode package version number. + * + * @return The minor package version number. + * + * @since 0.1.0 + */ +SRD_API int srd_package_version_minor_get(void) +{ + return SRD_PACKAGE_VERSION_MINOR; +} + +/** + * Get the micro libsigrokdecode package version number. + * + * @return The micro package version number. + * + * @since 0.1.0 + */ +SRD_API int srd_package_version_micro_get(void) +{ + return SRD_PACKAGE_VERSION_MICRO; +} + +/** + * Get the libsigrokdecode package version number as a string. + * + * @return The package version number string. The returned string is + * static and thus should NOT be free'd by the caller. + * + * @since 0.1.0 + */ +SRD_API const char *srd_package_version_string_get(void) +{ + return SRD_PACKAGE_VERSION_STRING; +} + +/** + * Get the "current" part of the libsigrokdecode library version number. + * + * @return The "current" library version number. + * + * @since 0.1.0 + */ +SRD_API int srd_lib_version_current_get(void) +{ + return SRD_LIB_VERSION_CURRENT; +} + +/** + * Get the "revision" part of the libsigrokdecode library version number. + * + * @return The "revision" library version number. + * + * @since 0.1.0 + */ +SRD_API int srd_lib_version_revision_get(void) +{ + return SRD_LIB_VERSION_REVISION; +} + +/** + * Get the "age" part of the libsigrokdecode library version number. + * + * @return The "age" library version number. + * + * @since 0.1.0 + */ +SRD_API int srd_lib_version_age_get(void) +{ + return SRD_LIB_VERSION_AGE; +} + +/** + * Get the libsigrokdecode library version number as a string. + * + * @return The library version number string. The returned string is + * static and thus should NOT be free'd by the caller. + * + * @since 0.1.0 + */ +SRD_API const char *srd_lib_version_string_get(void) +{ + return SRD_LIB_VERSION_STRING; +} + +/** @} */ diff --git a/libsigrokdecode4DSL/version.h.in b/libsigrokdecode4DSL/version.h.in new file mode 100644 index 00000000..3053cefc --- /dev/null +++ b/libsigrokdecode4DSL/version.h.in @@ -0,0 +1,70 @@ +/* + * This file is part of the libsigrokdecode project. + * + * Copyright (C) 2010 Uwe Hermann + * Copyright (C) 2012 Bert Vermeulen + * + * 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 3 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, see . + */ + +#ifndef LIBSIGROKDECODE_VERSION_H +#define LIBSIGROKDECODE_VERSION_H + +/** + * @file + * + * Version number definitions and macros. + */ + +/** + * @ingroup grp_versions + * + * @{ + */ + +/* + * Package version macros (can be used for conditional compilation). + */ + +/** The libsigrokdecode package 'major' version number. */ +#undef SRD_PACKAGE_VERSION_MAJOR + +/** The libsigrokdecode package 'minor' version number. */ +#undef SRD_PACKAGE_VERSION_MINOR + +/** The libsigrokdecode package 'micro' version number. */ +#undef SRD_PACKAGE_VERSION_MICRO + +/** The libsigrokdecode package version ("major.minor.micro") as string. */ +#undef SRD_PACKAGE_VERSION_STRING + +/* + * Library/libtool version macros (can be used for conditional compilation). + */ + +/** The libsigrokdecode libtool 'current' version number. */ +#undef SRD_LIB_VERSION_CURRENT + +/** The libsigrokdecode libtool 'revision' version number. */ +#undef SRD_LIB_VERSION_REVISION + +/** The libsigrokdecode libtool 'age' version number. */ +#undef SRD_LIB_VERSION_AGE + +/** The libsigrokdecode libtool version ("current:revision:age") as string. */ +#undef SRD_LIB_VERSION_STRING + +/** @} */ + +#endif From 941c73d3733766afde1e30229f9564644825a90e Mon Sep 17 00:00:00 2001 From: DreamSourceLab Date: Wed, 10 Aug 2016 15:12:13 +0800 Subject: [PATCH 29/32] Fix some minor display/platform issues --- DSView/DSView.qrc | 17 --- DSView/darkstyle/style.qss | 5 + DSView/icons/Blackman.png | Bin DSView/icons/Flat_top.png | Bin DSView/icons/Hamming.png | Bin DSView/icons/Hann.png | Bin DSView/icons/Rectangle.png | Bin DSView/icons/about.png | Bin DSView/icons/close.png | Bin DSView/icons/export.png | Bin DSView/icons/fft.png | Bin DSView/icons/file.png | Bin DSView/icons/file_cn.png | Bin 1362 -> 0 bytes DSView/icons/file_dis.png | Bin DSView/icons/file_dis_cn.png | Bin 1224 -> 0 bytes DSView/icons/instant.png | Bin DSView/icons/instant_cn.png | Bin 1262 -> 0 bytes DSView/icons/instant_dis.png | Bin DSView/icons/instant_dis_cn.png | Bin 1352 -> 0 bytes DSView/icons/math.png | Bin DSView/icons/math_dis.png | Bin DSView/icons/maximize.png | Bin DSView/icons/measure.png | Bin DSView/icons/measure_cn.png | Bin 1316 -> 0 bytes DSView/icons/measure_dis.png | Bin DSView/icons/measure_dis_cn.png | Bin 1414 -> 0 bytes DSView/icons/minimize.png | Bin DSView/icons/next.png | Bin DSView/icons/params.png | Bin DSView/icons/params_cn.png | Bin 1388 -> 0 bytes DSView/icons/params_dis.png | Bin DSView/icons/params_dis_cn.png | Bin 1619 -> 0 bytes DSView/icons/pre.png | Bin DSView/icons/protocol.png | Bin DSView/icons/protocol_cn.png | Bin 3269 -> 0 bytes DSView/icons/protocol_dis.png | Bin DSView/icons/protocol_dis_cn.png | Bin 1454 -> 0 bytes DSView/icons/restore.png | Bin DSView/icons/search-bar.png | Bin DSView/icons/search-bar_cn.png | Bin 1580 -> 0 bytes DSView/icons/search-bar_dis.png | Bin DSView/icons/search-bar_dis_cn.png | Bin 1888 -> 0 bytes DSView/icons/settings.png | Bin DSView/icons/single.png | Bin DSView/icons/single_dis.png | Bin DSView/icons/start.png | Bin DSView/icons/start_cn.png | Bin 1401 -> 0 bytes DSView/icons/start_dis.png | Bin DSView/icons/start_dis_cn.png | Bin 1495 -> 0 bytes DSView/icons/stop.png | Bin DSView/icons/stop_cn.png | Bin 830 -> 0 bytes DSView/icons/trigger.png | Bin DSView/icons/trigger_cn.png | Bin 1141 -> 0 bytes DSView/icons/trigger_dis.png | Bin DSView/icons/trigger_dis_cn.png | Bin 1453 -> 0 bytes DSView/icons/wiki.png | Bin DSView/pv/data/mathstack.cpp | 10 +- DSView/pv/dialogs/calibration.cpp | 11 +- DSView/pv/dialogs/deviceoptions.cpp | 23 ++-- DSView/pv/dialogs/deviceoptions.h | 3 +- DSView/pv/dialogs/fftoptions.cpp | 36 +++--- DSView/pv/dialogs/fftoptions.h | 3 +- DSView/pv/dialogs/protocolexp.cpp | 4 + DSView/pv/dialogs/protocollist.cpp | 4 + DSView/pv/dialogs/search.cpp | 1 + DSView/pv/dialogs/waitingdialog.cpp | 4 +- DSView/pv/dock/dsotriggerdock.cpp | 6 +- DSView/pv/dock/measuredock.cpp | 2 + DSView/pv/dock/triggerdock.cpp | 3 + DSView/pv/mainwindow.cpp | 10 +- DSView/pv/mainwindow.h | 7 +- DSView/pv/prop/binding/binding.cpp | 4 + DSView/pv/sigsession.cpp | 4 +- DSView/pv/sigsession.h | 2 + DSView/pv/toolbars/trigbar.cpp | 1 + DSView/pv/view/analogsignal.cpp | 1 + DSView/pv/view/decodetrace.cpp | 13 +- DSView/pv/view/devmode.cpp | 2 + DSView/pv/view/dsosignal.cpp | 6 +- DSView/pv/view/ruler.cpp | 25 ++-- DSView/pv/view/trace.cpp | 9 +- DSView/pv/view/trace.h | 1 + DSView/pv/view/view.cpp | 38 ++++-- DSView/pv/view/view.h | 1 + DSView/pv/view/viewport.cpp | 14 +-- DSView/pv/widgets/decodergroupbox.cpp | 1 + DSView/res/DSCope1.def.dsc | 78 ++++++------ DSView/res/DSLogic0.def.dsc | 34 +++--- DSView/res/DSLogic2.def.dsc | 163 +++++++++++++------------- libsigrok4DSL/hardware/DSL/dscope.c | 6 +- libsigrok4DSL/hardware/DSL/dsl.h | 1 + libsigrok4DSL/hardware/DSL/dslogic.c | 51 +++++--- libsigrok4DSL/hardware/demo/demo.c | 7 +- libsigrok4DSL/libsigrok.h | 3 + 94 files changed, 357 insertions(+), 257 deletions(-) mode change 100755 => 100644 DSView/icons/Blackman.png mode change 100755 => 100644 DSView/icons/Flat_top.png mode change 100755 => 100644 DSView/icons/Hamming.png mode change 100755 => 100644 DSView/icons/Hann.png mode change 100755 => 100644 DSView/icons/Rectangle.png mode change 100755 => 100644 DSView/icons/about.png mode change 100755 => 100644 DSView/icons/close.png mode change 100755 => 100644 DSView/icons/export.png mode change 100755 => 100644 DSView/icons/fft.png mode change 100755 => 100644 DSView/icons/file.png delete mode 100644 DSView/icons/file_cn.png mode change 100755 => 100644 DSView/icons/file_dis.png delete mode 100644 DSView/icons/file_dis_cn.png mode change 100755 => 100644 DSView/icons/instant.png delete mode 100644 DSView/icons/instant_cn.png mode change 100755 => 100644 DSView/icons/instant_dis.png delete mode 100755 DSView/icons/instant_dis_cn.png mode change 100755 => 100644 DSView/icons/math.png mode change 100755 => 100644 DSView/icons/math_dis.png mode change 100755 => 100644 DSView/icons/maximize.png mode change 100755 => 100644 DSView/icons/measure.png delete mode 100644 DSView/icons/measure_cn.png mode change 100755 => 100644 DSView/icons/measure_dis.png delete mode 100644 DSView/icons/measure_dis_cn.png mode change 100755 => 100644 DSView/icons/minimize.png mode change 100755 => 100644 DSView/icons/next.png mode change 100755 => 100644 DSView/icons/params.png delete mode 100644 DSView/icons/params_cn.png mode change 100755 => 100644 DSView/icons/params_dis.png delete mode 100644 DSView/icons/params_dis_cn.png mode change 100755 => 100644 DSView/icons/pre.png mode change 100755 => 100644 DSView/icons/protocol.png delete mode 100755 DSView/icons/protocol_cn.png mode change 100755 => 100644 DSView/icons/protocol_dis.png delete mode 100644 DSView/icons/protocol_dis_cn.png mode change 100755 => 100644 DSView/icons/restore.png mode change 100755 => 100644 DSView/icons/search-bar.png delete mode 100644 DSView/icons/search-bar_cn.png mode change 100755 => 100644 DSView/icons/search-bar_dis.png delete mode 100644 DSView/icons/search-bar_dis_cn.png mode change 100755 => 100644 DSView/icons/settings.png mode change 100755 => 100644 DSView/icons/single.png mode change 100755 => 100644 DSView/icons/single_dis.png mode change 100755 => 100644 DSView/icons/start.png delete mode 100644 DSView/icons/start_cn.png mode change 100755 => 100644 DSView/icons/start_dis.png delete mode 100755 DSView/icons/start_dis_cn.png mode change 100755 => 100644 DSView/icons/stop.png delete mode 100644 DSView/icons/stop_cn.png mode change 100755 => 100644 DSView/icons/trigger.png delete mode 100644 DSView/icons/trigger_cn.png mode change 100755 => 100644 DSView/icons/trigger_dis.png delete mode 100644 DSView/icons/trigger_dis_cn.png mode change 100755 => 100644 DSView/icons/wiki.png diff --git a/DSView/DSView.qrc b/DSView/DSView.qrc index b2799fad..f58808ff 100644 --- a/DSView/DSView.qrc +++ b/DSView/DSView.qrc @@ -36,25 +36,8 @@ icons/gear.png icons/wiki.png icons/wait.gif - icons/file_cn.png - icons/file_dis_cn.png - icons/instant_cn.png - icons/measure_cn.png - icons/measure_dis_cn.png - icons/params_cn.png - icons/params_dis_cn.png - icons/protocol_dis_cn.png - icons/search-bar_cn.png - icons/search-bar_dis_cn.png - icons/start_cn.png - icons/stop_cn.png - icons/trigger_cn.png - icons/trigger_dis_cn.png - icons/protocol_cn.png icons/instant_dis.png - icons/instant_dis_cn.png icons/start_dis.png - icons/start_dis_cn.png icons/settings.png darkstyle/style.qss icons/export.png diff --git a/DSView/darkstyle/style.qss b/DSView/darkstyle/style.qss index 6b3fc4ea..21455512 100755 --- a/DSView/darkstyle/style.qss +++ b/DSView/darkstyle/style.qss @@ -881,6 +881,11 @@ QDockWidget { titlebar-normal-icon: url(:/qss_icons/rc/undock.png); } +QDockWidget::title { + border: 1px solid #282727; + background-color: #2b2a2a; +} + QDockWidget::close-button, QDockWidget::float-button { border: 1px solid transparent; border-radius: 2px; diff --git a/DSView/icons/Blackman.png b/DSView/icons/Blackman.png old mode 100755 new mode 100644 diff --git a/DSView/icons/Flat_top.png b/DSView/icons/Flat_top.png old mode 100755 new mode 100644 diff --git a/DSView/icons/Hamming.png b/DSView/icons/Hamming.png old mode 100755 new mode 100644 diff --git a/DSView/icons/Hann.png b/DSView/icons/Hann.png old mode 100755 new mode 100644 diff --git a/DSView/icons/Rectangle.png b/DSView/icons/Rectangle.png old mode 100755 new mode 100644 diff --git a/DSView/icons/about.png b/DSView/icons/about.png old mode 100755 new mode 100644 diff --git a/DSView/icons/close.png b/DSView/icons/close.png old mode 100755 new mode 100644 diff --git a/DSView/icons/export.png b/DSView/icons/export.png old mode 100755 new mode 100644 diff --git a/DSView/icons/fft.png b/DSView/icons/fft.png old mode 100755 new mode 100644 diff --git a/DSView/icons/file.png b/DSView/icons/file.png old mode 100755 new mode 100644 diff --git a/DSView/icons/file_cn.png b/DSView/icons/file_cn.png deleted file mode 100644 index 00096c509581fc2426e123cf58cdef3c27234a65..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1362 zcmXw(2~g5m7{DK(sFWaOO5zok$7&mCZR+ZJ{Z+I9jhxk-B3%Qe5?I8{Rl#oTGIv2& zOHDVmY_&u37&WtOJR-F`YCYS5h&IJr$};y)O*8L%-^}m(ec#NRdGBR~(}ImL78n44 zaR>#X!Y@UG4<$$$D8|+|?(eVH-FO|=ZPY{5=adJ z!{)?;2Sku&{FC-8U_Oo0!Pt2*9E|w*_Wsofg3s9A<*+n&%Dx=vjq8 zeh~@BCZk#o5#tHMv8j1Q|97r$+RUuai|(0Dl*z2U%(kr`(sYI=I9YkgtcF@e0d9n7 zHJze1aa>=>Ss$$~(8E|OhbChr;;y?FlBJv9&fm96cM$zlZ2CbX$Cnyh{HST3(iFW} zUdULJWyoM&zh(qIruaT57*Q;4ka1g+Sn=_1gcW@aRS_Z^S#*{8Q`THsS%0>(vvY9C z9;~k;0$h5^+Uo5++Q?!#mia>7!!t885zUPKZq%AxHwT2Vs+C2pHY_hU*WF?=Dg4jm z%zU@Bv^1@Qg9G=+i>TpRSb`^Pk*~#z4aI8N@MJBGMza<)jv7BAAIZ(ji^JnRk=7D= z#{pMIvvUL@5tX@W7q7iMaTs33?iUqGeuE7bhfCS~BYP%9 ze)@qGRMB^gUEo1lnwX!T-^pSm)D4%ULX`*+Mjx-eD)GNLa9rVD$9tSYj|KIdh!Js|!+k$7_DhtYfXTo_^yh zsNpUp#+oc}r6RBmH2Gw9G=eeoGz6*kwx;}*9@#F{BhQs5fZIEv-Ox7un8=oj zK<0oq7es9;^~&^7tReSfT`A&soxighB{Y(2kG!Y3L1^$^Z~xgY^2o#T-hdZ>+$@+p z^kOmG?zSq(0H}V=J(w*PL&_X+6S(aQU6U-?ug(caiq$x+B4zKEJdkcn>H(iBsTsk< zFjPH)8e?hb{E7>3;)GC0gLvQFyii52tfp>0q!^ddcSz_yXOtG_$=kt|x^_R0iSEXC zgAc-%XU8)>JGqTFxz-sXL**mtzSQG-lJv#AYSasQ8zX*0X*&=mt(wghd)bxZPINFF zs++LjrT%o=q@@ia6P_>mEezry-k%d&RDq+xUk3;Yq(RmG HF~|Q0GjCVq diff --git a/DSView/icons/file_dis.png b/DSView/icons/file_dis.png old mode 100755 new mode 100644 diff --git a/DSView/icons/file_dis_cn.png b/DSView/icons/file_dis_cn.png deleted file mode 100644 index b6e24584d2bed42577493c96ca1c70cc01f42a92..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1224 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA1|-9oezpTC&H|6fVg?4jBOuH;Rhv&5D9B#o z>FdgVpH-CK+`v}yw-Q*$HKN3^v?L?Hh+*ZrGaf)8o)VytOKNd)QD#9&W_})nFsGt2 zP@D@S?wnsxS(KTcQNpl>|BMw-ln*Rgl$r=qs!)=do62DNou3mZ!wHfJ&QB{TPb^Ah zh%NXJQqB!FJhM1CClRQ$h++GIE&V`Y4v=toVo_dZUOK}8>1QA>utW5h7A2=LeEr}2 z8m7!SKQ}iuuY}>-nQ8lhVr(E~A(%zOF+?uJ1IBi3y-~A^=ZTH*@=%{`INpdb9z?1sxIVf_5E=BxKiP@ zrvFY~Si|h$74LBE3u{Vms?mwhdukc>F@JcNz3ZRm(mkGV zGs88T^S|W|ur;*3-le6>UcIs99!ml50p?>jN*UY_8~B`kANRJAk%4{56nW+!PF|H) zKHE$?*8ROon4kHF$N^V|l^rJ@iAb>&@Ey=;D4*1I;+2Ff%LmQ|{f(N3Ec$d_AK+;C z%y2GeYww9&6P_~MU}*D+pRl#z2+(o9P&ZC&*tyek$GYEvw+_y5nDi&?qm6!Vy^;Nn zb=JvN784uFe2(r|=RN6%Z!UktwY>T}d*@t9Q_$Y!C9P3gfo0@JF zyWJ}ATle%WISJQ^?{cGCRQO+W4Hbi&@~rf<>iS&iYGH+*+>zWpD`{?8ZW042sM zO^x49Yq}O#?GKh;I*mW9Mv&{F{W{Iij<;7G&XnKs@lbo<@{XS&dxCEDatDvUH>-ml zN<`@CT+_}6YWwx@(^QqZ+b>tvgfSc5pK@d0_8)c0tCwE*@VDKmW54z0KTP_*bJm%E z-NM8wEwMi^pY1Z2#L_o+RbBplG+~jr+UTqJMMPTf>=eIA^Bd1T70Efldh}EG9+N^T zhAFE=o%VlR%_N(8c&X&-hp}1y4%SzscD}li9KZc`*;+mk-75Bg#k(Ts_vLPF+q0x0 zFiZ6Pk!{uG^Xu0&9A zX7*F3?w%FL8^+`Fq?Uh~m?>kcuedaeHNNBcy|$PTo&6^))%%Q#G~5FlbIx%*T+imc zD@Q6|{TG!5QXQ4|^KC+Ozopr0P0f`>Hq)$ diff --git a/DSView/icons/instant.png b/DSView/icons/instant.png old mode 100755 new mode 100644 diff --git a/DSView/icons/instant_cn.png b/DSView/icons/instant_cn.png deleted file mode 100644 index 0e5860945bdcd8295e5349b56df5d1b62eb57f15..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1262 zcmXxje>~H990%~vjAOJdg?dCxQ#zG-a4Y$7P0a19g-S*U$HopD^YuWkTGCzD4l|*h zN>`^Nl8b0vw7R0zI%~&6n#ZPV(H=Ew9ye*jeb+tikN4;O_&ndQ&*$;@{PFoAHu@{9 z{Th1!02ZP}>_xhtrR+XKR)*F5_ejI!(-I2-z}>eLs%{g`6+v4V+D*09*f`_2kvbiX zqo0VH0EZR9*<2oI?U2(DS_UH&!7R++vGZXzmjhN}Jc)?gTRfbbU&>==Wy7EyFAqW# z_epuobW4-~v-6mMJcq|16O3iDkIRCK(|JsglD}k$JAAsFU6_%ZjzoE&SK3{Tu$_e& z={ydblLe%%la?pg{&&8Jm%#-8EFFLIe=MAvm&fG5z$mZ3gV<-5SR5P9WrCJzr8fX* z2^0|)mwim1FqD(Le|4y4*Lk)~n6`}WQxheP-P7splQ`^=@G>ggB~j;9Lz7>;G%U-# z=-}*m%ny5lLJ%ZYI5}Py5?)`%9vJ=nBR;~q^6S_EdiGDxw+~V^6K?{cEOpsSFBq4s zmiKA;G}|=`>V^086;=Lc53RHle8)d@$@79byW_zHfk5DmI%}oS?$3*+)4xr1NEJG< zbu%;DL^C3Dr8f1T#6LJ#<#dAtk;N!90bFId%$8>!Jgf~g%uvE9eU1r~5O=PxlYN%I zn4TqEM5EE1pJPk5*3d%SPNSFa8`<1~&BJsEW{(dL$xB8(^4eh2RdHRXbXpDKXIZkbI zjO2sy({X-X8v=4{{M0H{lJscSixLK7vinsrqkephZs)+G2N4kMTfpaj)y-M#qnvMN!Ss`swK-y9`y;Zjf?GGiSodT-(~( zNRb6Dr?eC@nOsz_ExL1Zfx2R^il0kqo9Ed(t0C4Xzp z$B3rzov!47T3E*DCF-1^OX-1e>-m`j9^`-|D-a+MkBx{<)}O0u*;5Aw3zYGmZiCP& zlXy(zU2?s{ihp$WR&oWzy4_QzY)!VOp15LjW~>?*fLb`buibmzj^BXNhBhwQgjwf; z4{~`lEodqy2s?oRZ_ZcV8F~SERx8ST!z>B)VW8k_jtMqBFY|3oj460!h`&C;W<0$*_2ow2Yt4;&B$e_m z)0OeZ?iEQlOwxwtOtU0KUR15cf}t~s?Kkb6Q4_P@nB6N*aWX3%c4MC|kL8-t(w_0a TMts0l|poNYGv*yagTr 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 diff --git a/DSView/icons/params_dis.png b/DSView/icons/params_dis.png old mode 100755 new mode 100644 diff --git a/DSView/icons/params_dis_cn.png b/DSView/icons/params_dis_cn.png deleted file mode 100644 index fcb8a6115a2f9bab5859f2a4023ee9e55595a4de..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1619 zcmXw(dpOg39LIk%Y;)V{7>!&ysx`Oiap#mfLoUg^FlMv4ZOzXm`rj@ zlT;IF;PR3)Najnvq#ziCkR$+5;YkBIXP}}ffiTeB%LVpI4vn-l#BI5r@DsTNgAU0c zmCgieIedZyDS;B?Oim&(X$+7?PXvBLY}1o)1u1u;GmbE6F;oy}K=N%QROKgSk|U+6 zE=Wrt1Lo^UgrtLzb`H=fU}_|j41_T@q`KnI;j|=De56EW0`1(^GZGA!;-E-oA}uin z;A+lFSAhM0Kbc7)1D`f>7k_C^^n?U*A_%PU&)=0Wd8y`41LMg+&C+8m0LW>(J3IJO zbKYN!h*u3z?fcWTWio8wsl1q2}dRSIx2i)9n4Qg1K%h4Ki$SB^Lrq28ZgO0iANyoj+eJUD(yj@_5l zKl{>pz0`E>_WJXrGl;T`8A#0DkyN)VDwpZSL?H z)UBXQMpbTf#` zE9Pqr7PigpIrMBJbmvLW+O{Rm(nJ?( z`XGQK2W4Aj6AeVeZ{5aQo*}xX9`ftd#x{Lg?SV`K3zNLpjwkGZYpgk$@r?e!>X4zD zc7=e&2<61`SNJNkYh}hYvX&oZh9i6K4<~ZsVlz&S`RKU|MXzxSNqf(;oeV^t27-DyB z+F-MH1~?3zgUQSPFjFF3Cx>XPkloxKEQf#X$wzKf((kF)j?m zSj3F53w!&AlMV8HVoZCT&f)yEhGXA~-2qGG?|oDE)pIVDK7H`vWR|bj?D2Pl(OdK8 z9oI#o$~`?n4{2eVVV4J$mhf@KExs>x%+gkF!KVAcAj50RfXQ^h1;5&0&tPo+runuR z$@1qqyUR=0PT35;1zT!^tK+6BjBQ$DEZP0_LoFdV&oGL*YVxKn!{}7p>Q1BP4@CAo z_=x{r3daIH$f2gSW;PxREWBBq{t}stYRcl%cd@~RVxDP#%0Cf`v=~rz#&P~=o_CLi zJf$@tZg+`RdEs_swO`TuS(Z?)(nI*-mhXpsHf5cEH_MDEM3A3oU?xn=BG>e(j>x&F zg3r);@aWSG7kpy6TYJ&fA>qD%&mIfZo9GCr=xnNL2)1N5#$}ah4^eJLR2KV3jotM0 z5$e~}I9?U<*Y3A2a99S((MK!}{Bf<2aFh8OpNZtd3?>2x<+!CBYgIh#_jQRz#>qu=uP(utkufDYiVfAkw8uDFU|8VoOUuU;mi% zYtK%9_uk#@-QHc(Z?f6@J9B2{%=b4lXXeb@8^S0D&@QR1)pm?ZNZJ9o6SzgvR;{*Y zQ~}ryxK#$=npWF1ssLQ?$a@+1K&x#UH2~f#2jHWvwrSKrQU|cak#}ROZ5mYoraAH! zOX`sHs#e=IDj=y7c-hEXC#eV66L<Jj-XFVinf5BFKGu!1@K!-2RNbCHjgL(r@FQCCrq~3uLULT1Uzc8=YjoN zYxQP=r18MlfLnn(fVq<90pFF>W4-Gmt_OZAgb;wso$O+hy%Bi9WW7LpYg;!=khB$W zt_{Juuyx*_-Scl;8Mm73Wl2Ywe6n*Yzx=;*Dw4V+&1*zl9YS9M_6Z>bU>D$VlRv8s zPUWjk|En!+aI>H^@m~h4%{W;OOa~@O+DR$S_c}>u0Jqg3@usp|U;^->$rmU&l?eRG zWJsFa%C>JZtU=$;NO%@FUDCGo@&YFUPgIb2jMF*A$!D0X9aw0x7l8ep{z<@T!`I>_ z1GqNtLBQ@Iq&=?b><&B(JOkV>7bLJf@87dBRs-)VbtHYmrCnmO@xT(`D&RH1TY;NR zXBBYZ=H_D106v^Gd};{mL-JFBHNYvRdl<0B^!_MmYgf*mz++ii>m*G9Ug2~;4t!p= zt&_B^^|(J(^x|K&f|CKmdeyVe;48{BNo_J`VsluRii{%t2e&tw-k*|$x$2Nu2u{v~rZmi80iG1AWEa7k@IH*mi7 z-fsYB0Y}Q#hNBLo^V*V>V?#*02c;dv?IvFf9AWy`IsH>j*6n)n&8=)t6^3<-13VdH z93yF6jIlk0P)Pb_Q55IeTsJSqI9Ad#A%uxB#%DqZcZCopN?ILbJT%65FYw_ILPv~o zT8weElJhw{gfK3IFeZf19%Ec8X-NoSeo+(;3}?GG8`uynS!vI2w&$}#NLxDKzb3mE z*gkVAi-A|V^4}BB1 zrh7WsqS?}?17}H^SoSVSuL6!%+Lhc>Vb5Z<(i;Q2D=iJ4srR<)5PG{S>vSidX0ly@ z$AP~|+8Q_<+tNARh1h9aJtzfWFY#+d%YPrX<=dV(UIeQ?(> zEZ>qckM*)YnS3p_OR>D)$@1MWRQr`~Gd$HEn%B`JWpQ8$LpRpdD^)48BooD3Q^)E+cLmI3>kk(W9HFY|eTw^cb7(yn1DFb3EqOPf|v z_qa^wOeL@Tw3FRbk#8INM{rsjNCjRGEHh&sl(eJu&YN7%I4R4sQAF4^XcBNhCOfC1 z-gZ)eq5Rte_;NPSe!Ygxe{nkVhl`s5131nN+nbf<$Q^+POtu1elheP(HK3RNJBW$E zyD|a~ZczWOz$00nKdWL6tf>F#X7e#5WW)ASlkEfi$7GMmyeM#<(>>p06M)Z>4t(C7 zkujk`-M+jPr1)da2orFn(|@*^JPaAy*r^;JLI}Xo-l>@EeAj@J^Rj@qWCVV!Ufo7~ zpZ!g3WGir|E4$J`wFGibMQJ_wh7i&k&}8%M`FoC^qw;cq-7*5d+o0ZYO1B)kN=H^l zcPm^N4EWuqw7KcXoXW#&SS-%~eqf*dB>t8fy`0Ysv8ls{ z%&FX>P6bj#WC9xwhSsP-s2!M|HTdcpb?q#vQz>AuOO3MH z+4x|+rwL6RKFFL3a4jj=;J02sZIqvMF0#Vqfpo36a@g0gqZ9XqL@Vo;LvPt;1hALV z_URgD|8ii@0p>r^4`ploodcb_fK$j~n7}`3_0$mwNt3e%oH>~Mz{T0n6YwH%Iq)9f zHLk9$Z5??Ab}ul?_aEQ@(;blzUEyuHBjE`OJ@_^ zAuNb7E+2GDS_EKrjIl>jUr`i$*U~E&+aZKiA%qi(qIhvg6-pYLcjv5YFC%@~c~sm% z24Fhwa`bzqEf^*}2e=_rcgg-tFb z>w#a%(mH?{t~XBKb-WxQ90a|0-&=@Z;>^& zE34~rU^eNi|8s$v%I+bS0M0huW%mE%dIXl~nXMg}&TRXeWPh`O*+VAKbf&moILTEu z-O1;(;X?US>K|U}Bwfs`LYLF&tx~XZ`+W@X_R-H5Y`Ee6Pu7)-ZlL&~5ehz{l!iu^lhrzA^mE)DS=yWs zLV0NaI!lb|J;`SAIJiK{m&*#S{PC^dmz}z)*%sSwd&x=`*1vXy?Sv+SXr&JnEg zUflHBZ0_oH^^7gkoqUxxeBI(gSN9C3>n~OMInc{5%-Y|P*Uwv$>$f(mUdDXKD-FbOGHM4~i0qG*SPqSmy= zR;MVz5c?9X>2%OhJB?bZssN;H)a96|#s75mN#slr!E z#bHINHjO~U0cKl@AYlb0s$3%DY1gn+9N3N-TS?+UTB2w%oh zNd!_bz*B!Gx`NcV{d8&&4iFr~Fa59Ck%>eci3V);Wp@hMVUgxRpoQUpvX6tO0YEa^ z$sXYm61S4;8m8``@+{V5iR+tDU;MDpysXqZ;aNfhDMR4=GgyafA%n6;z~9;`y&056 z!H~a6pcx?5hg5{pMo%1NW?XB#E!6 z+jgyXr*XLyqmE0q9Et4MYRidRbc07c)_%XltelvXFa>X=!H0 z9bEJVibrH8Ha0eXFDfcp=b8K}H-oSB-irwg3~Vm#3VA9Av@%V25n_B-UJkE88z=rwgpDK=uOycwWlT=l9 ze*SCOHu+`AnA6~>qfzqXn&0nBeb%f2v*fJ+L)@9XP6!kc0hzMP=MCN83pSlptV|(x zPxCBn9i?smH6ZX2ybJLKf%i>bmxiz*bRuHXkLp zr>G&xJcAi)ZKWFBMZx~+tLnS{OxN-|=G&u>hYx9~t&GcRYJkCCw4!VRI?(#Z{0nk( zo2CZ?>VtJm#=ZGy>49uwZ-al*fra?Wm;K>X!U$6~Q^GtaMY9YVA{EEr1rIl>xQ#vi zR6nZ^V3v{Ct+jHGA39Uw;zeDGEOIG1UpOTF`kY{I zGN~CyzLg;0WM$`MwL}`_RP7Z8_){LR1b(LU9m>dD7L)GoOn1~hrxkJaB!0Pe;{!|* z+0Wx9U-Ls0RNy-!H5?Z>$D;|Ae?{3Lot|C)5r3T(4uZ0VW3Knv5TS3cq(Mtgn~?aU zOx#3C15uXWkcfjSI(f4)qE3oeq}`}n{5xDIOsU^fMHsU2T@Nsu_)OE-l)ARi$cfhC zw9;+&UnQITE(J3)c5Zge?@}Ht$2Ujc2($)ar&!L~9-u1d(1Jm~-|7}<#B<$~-*D~l zt@b&87`LW1O;0f(w>b#CKl5tZry?GX4|N zkWp00%DFh1ac5%8l5GRmpU;4Cv{qH(-892B98bL1ldsN}a4$%QNC8IZb$3vLPtpys z$1|9F?>v4OOYx~Pv#n`HWW`Xj?vi?9H2;sG z5+D)2z^^cuic!*|uLLN#ao2V|ZBcpC#%#D~CKVng6w!|iI61i3bC3Zy{{ty3 Bmcjr4 diff --git a/DSView/icons/restore.png b/DSView/icons/restore.png old mode 100755 new mode 100644 diff --git a/DSView/icons/search-bar.png b/DSView/icons/search-bar.png old mode 100755 new mode 100644 diff --git a/DSView/icons/search-bar_cn.png b/DSView/icons/search-bar_cn.png deleted file mode 100644 index b8a249e4352d7a09d6742fc7489dac78701cd31b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1580 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 diff --git a/DSView/icons/stop.png b/DSView/icons/stop.png old mode 100755 new mode 100644 diff --git a/DSView/icons/stop_cn.png b/DSView/icons/stop_cn.png deleted file mode 100644 index da13d8481798454b9dbd278590143f710b8dc76b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 830 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA1|-9oezpTC&H|6fVg?4jBOuH;Rhv&5D9B#o z>FdgVpH-CKTtlxkxdbfa8d2g{T9T1p#ISPR84sWkPYF=SCAB!YD6^m>Ge3_(m{U<1 zD9!~Ecg`=UEXqvJC}CK`f5r+Z$_ExLN=*bQRVc~KO=U3s&d&*y;RMM9=ckpFCl;kL z#1{MqDdz?oo>`onlL%B=#IXIqmVTfx2S_+Pu_!MyFP-6l^fQnb*dh8$i;`0rzW#51 z4O8ZvpPQSSSHke^%(VSLF*cC0kj#>tRE8DrE~zsxFmZaiIEG|2zMZ+hE7(!w*#7ql zw_ZePXhcUvab!=-vKC}omk^hr8>8X7=|yBH<~+CekTTcK zD752#KlSu3mG9?bB$z%oXX?!~eqNr=XI$>&8Ii72%Cs@1@d^aRaJ;?o`m5QlyYFNf z#FdZzx0UO^&)s|yz#uU zOM>(PQ3hGj2{n8SD)H0K-}!st?)t40pBPDTig4O8f8abYZ^`0CvFVLVCN5EC_Z&lEqVFTKVdvYmbNlDDJvSicFB5`?VfVx zy}{LNXL9^LZC#SjpvPRXT6N7TM$g4XH)q~V^Z3-u!qD)9o8j{3Nh_2xMJD-hEL?t( zwP86!+|<(xx|XmuxVs9n-;cYl_^kTDjWcGA^zTuT-nwhGu_fL7pO}912 zJfd!{0(vNNfvfczk$3NI-q+LqV(j|=zFnaD_CJo3LBVN%^JPWptV2hufysoy)78&q Iol`;+00qcRwg3PC diff --git a/DSView/icons/trigger.png b/DSView/icons/trigger.png old mode 100755 new mode 100644 diff --git a/DSView/icons/trigger_cn.png b/DSView/icons/trigger_cn.png deleted file mode 100644 index 8f98b4dd629ef727038a2e1be09ec920c443df94..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1141 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA1|-9oezpTC&H|6fVg?4jBOuH;Rhv&5D9B#o z>FdgVpH-CKTz%iEx~E_v*N76w(vpn)B8HXg&UgTYcuIgmE~&-IMVSR9nfZAP!kmiA zKyfaRxO09%Wl?5&MhU|j{xeoUQ9iI}QEDPcsX|F+ZYqQ6cYaQw3@1n?I6tkVJh3R1 zA-3Q@NI5sy@XX@moJ63~B8Ke;w)6vqIY7eUiA8ytdFc!Xq@RJjzz)%0T9lm1@b!Q5 zYnU?U{M_8syb^|QXQu53im`!|g=CiGq%y2{cS)Urf!V>+#W5tK@$HQB-BUv)j@#ci zm*hOw#m2QWcaPv^BaM~YZths=R7}%eDW7S( z#QQ1HYcvWScj>Dy{x9O*TiE~Z(;xe)SZT;mVUp`oQFE)(4inwJUH6dh4W?x$(lv6#*h!%7YL7)K*%z{C;|NU*yWGXB+S5{6Dkz z^qutwTyG1{E|s5j+kV?D*OdZVySQz2rgZk{?q^=xup5pM+;8u0kUg-pV+K~!PY z1BRZPC#89Rvz&Dz`FDp<&V0Z7`{kRDYiYGz@NtRpdw9t^H!*~7iQVVsh0~7zI*@s7 z_0hG98caQR`thCN{k{DCT}DH7_WtWuOsk$9_`K~r^L?i3;^b+m`nTROz4v_Ob|m#d z?Slz#_cv}=jb*m$>&WzM7e46c;*%Qr&D~Y5JkXSVS>x;j?{_+0S`)tg<*Fq%_VbwI zrkC(p%t*cI|ML}dbwhe?z2ZyOgpH?8Zea^`)>uD>`Qe6T9IE?UJ7#>^n{~&3x5=B^ t-jCOCE;qX4Wvh9c{j9T#_JYhm>^zd^FK*cwWCF}x44$rjF6*2UngD((_3Z!v diff --git a/DSView/icons/trigger_dis.png b/DSView/icons/trigger_dis.png old mode 100755 new mode 100644 diff --git a/DSView/icons/trigger_dis_cn.png b/DSView/icons/trigger_dis_cn.png deleted file mode 100644 index e17bfce7150364663554979b53aaa7a5bc6b77d5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1453 zcmXw(dpOg39LK-AFveWgWg10WsdE-nlyZdlY0TM`lPE<2XT=EQ&%+Utobj#hIQPHYmF9TCX`)%tQf71jGexh#g- zYQ|$nv%q$xzP3uyRx3W7L%akAmj!}jm(_NiAH&)4%qWJ+$_2M&lCvtTrN;Xh+!%ID z1dtgk)KB;k&4%p3+!_J>&2ya|X`U!@@Fv3u2kw#x$0q`V zfPd?&sVpLq_`qdm8YE(G;0V*YhEil$hVU6o7nV2?w@V)kykl7wP-zGwO+i`%*`PPc za6!f&GUW#^!(?Uxj%(RKeLR$a6}3Ih*x3z@Vg-@>7E0Www=)Y(`EF%k6e&2XKbqV^ z1JODGoD-!=rE&=t3F@ISGJy+|h{Y#6y;A)l{EHY8>ZnE8BvM8uq%Wp6&ww3kAO3q_ z918d7mz)(w*8kKLg0ZopL%t@aruRIceF@eZ+f(V3TU3Hn>mA)R7 zNBkiR@U44VpXStfV0G|d4rJ3QcN7UHl}o#I!W3d*aa4WwcH-!J^PS_V4xy(Zf~CMf z(Cgn-;a&AT-Q{Js))(y4z4iL9L5POI8Y3g4g9-OmLFCGg5o;KpGitKZK&TmV=W+xdU@>AgET#p z4JW|oJV6wq1l6o9J8Oytl9@iWUV+mGl|9H}%;7(SG>WIQtT-2EBY~bti)U$4-Cp@am>WE$NY)tUgpcXH*ii#4O@QYkn&Cq2r6w~^$~h5_)mtR8 zgEUXl)cLmJ?d7J_&MM|g-D4Z;cV{?#<#zwIw)qb3G$nL^b2S3Zy6opLqTF0y9-vbe zwA=2bLYkM(--;gZnnm97=z-_JFga!;f%f@Ect;<1e{s-#|Guw7fd!(1ZK22k#C6;G z#r;>luM@SCQ8rt+m9u!nCNul<%rg?A%m-O_p?1_v&=7Lx(1kP{B6hT)@CD~`-;x8S z_Zi})snpQ;%9RPPq9NnlD%msAIibmyYp96s_knM#d44YQN1RpBG1liEZ>7{X)iP=< ztQ}|L%#N(l$z-@)4@x;zu4UqCpG1zQU>=6Z(uEJKvQzdvp{RZ(;OsetFixedSize(400, 200); + this->setFixedSize(400, 250); this->setWindowOpacity(0.7); this->setModal(false); @@ -55,7 +55,12 @@ Calibration::Calibration(QWidget *parent) : _exit_btn = new QPushButton(tr("Exit"), this); _flayout = new QFormLayout(); + _flayout->setVerticalSpacing(10); + _flayout->setFormAlignment(Qt::AlignLeft); + _flayout->setLabelAlignment(Qt::AlignLeft); + _flayout->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow); QGridLayout *glayout = new QGridLayout(); + glayout->setVerticalSpacing(5); glayout->addLayout(_flayout, 1, 0, 1, 5); glayout->addWidget(_save_btn, 2, 0); diff --git a/DSView/pv/dialogs/deviceoptions.cpp b/DSView/pv/dialogs/deviceoptions.cpp index 970bf4ad..74f8f557 100644 --- a/DSView/pv/dialogs/deviceoptions.cpp +++ b/DSView/pv/dialogs/deviceoptions.cpp @@ -45,8 +45,7 @@ DeviceOptions::DeviceOptions(QWidget *parent, boost::shared_ptrdev_inst()) { _props_box = new QGroupBox(tr("Mode"), this); - _props_box->setLayout(&_props_box_layout); - _props_box_layout.addWidget(get_property_form()); + _props_box->setLayout(get_property_form(_props_box)); _layout.addWidget(_props_box); if (_dev_inst->dev_inst()->mode != DSO) { @@ -118,25 +117,27 @@ void DeviceOptions::reject() QDialog::reject(); } -QWidget* DeviceOptions::get_property_form() +QGridLayout * DeviceOptions::get_property_form(QWidget * parent) { - QWidget *const form = new QWidget(this); - QFormLayout *const layout = new QFormLayout(form); - form->setLayout(layout); + QGridLayout *const layout = new QGridLayout(parent); + layout->setVerticalSpacing(5); const vector< boost::shared_ptr > &properties = _device_options_binding.properties(); - BOOST_FOREACH(boost::shared_ptr p, properties) + int i = 0; + BOOST_FOREACH(boost::shared_ptr p, properties) { assert(p); const QString label = p->labeled_widget() ? QString() : p->name(); + layout->addWidget(new QLabel(label, parent), i, 0); if (label == tr("Operation Mode")) - layout->addRow(label, p->get_widget(form, true)); + layout->addWidget(p->get_widget(parent, true), i, 1); else - layout->addRow(label, p->get_widget(form)); + layout->addWidget(p->get_widget(parent), i, 1); + i++; } - return form; + return layout; } void DeviceOptions::setup_probes() @@ -177,8 +178,8 @@ void DeviceOptions::setup_probes() ch_opts->setChecked(true); } } + g_variant_unref(gvar_opts); } - g_variant_unref(gvar_opts); } for (const GSList *l = _dev_inst->dev_inst()->channels; l; l = l->next) { diff --git a/DSView/pv/dialogs/deviceoptions.h b/DSView/pv/dialogs/deviceoptions.h index c2dd9ee2..6de30d94 100644 --- a/DSView/pv/dialogs/deviceoptions.h +++ b/DSView/pv/dialogs/deviceoptions.h @@ -61,7 +61,7 @@ protected: private: - QWidget* get_property_form(); + QGridLayout *get_property_form(QWidget *parent); void setup_probes(); @@ -86,7 +86,6 @@ private: QVector _probes_checkBox_list; QGroupBox *_props_box; - QVBoxLayout _props_box_layout; QPushButton *_config_button; QPushButton *_cali_button; diff --git a/DSView/pv/dialogs/fftoptions.cpp b/DSView/pv/dialogs/fftoptions.cpp index 3b08406c..d1df974f 100644 --- a/DSView/pv/dialogs/fftoptions.cpp +++ b/DSView/pv/dialogs/fftoptions.cpp @@ -169,26 +169,34 @@ FftOptions::FftOptions(QWidget *parent, SigSession &session) : } } - _flayout = new QFormLayout(); - _flayout->addRow(new QLabel(tr("FFT Enable: "), this), _en_checkbox); - _flayout->addRow(new QLabel(tr("FFT Length: "), this), _len_combobox); - _flayout->addRow(new QLabel(tr("Sample Interval: "), this), _interval_combobox); - _flayout->addRow(new QLabel(tr("FFT Source: "), this), _ch_combobox); - _flayout->addRow(new QLabel(tr("FFT Window: "), this), _window_combobox); - _flayout->addRow(new QLabel(tr("DC Ignored: "), this), _dc_checkbox); - _flayout->addRow(new QLabel(tr("Y-axis Mode: "), this), _view_combobox); - _flayout->addRow(new QLabel(tr("DBV Range: "), this), _dbv_combobox); - - _hlayout = new QHBoxLayout(); - _hlayout->addLayout(_flayout); _hint_label = new QLabel(this); QString hint_pic= ":/icons/" + _window_combobox->currentText()+".png"; QPixmap pixmap(hint_pic); _hint_label->setPixmap(pixmap); - _hlayout->addWidget(_hint_label); + + _glayout = new QGridLayout(); + _glayout->setVerticalSpacing(5); + _glayout->addWidget(new QLabel(tr("FFT Enable: "), this), 0, 0); + _glayout->addWidget(_en_checkbox, 0, 1); + _glayout->addWidget(new QLabel(tr("FFT Length: "), this), 1, 0); + _glayout->addWidget(_len_combobox, 1, 1); + _glayout->addWidget(new QLabel(tr("Sample Interval: "), this), 2, 0); + _glayout->addWidget(_interval_combobox, 2, 1); + _glayout->addWidget(new QLabel(tr("FFT Source: "), this), 3, 0); + _glayout->addWidget(_ch_combobox, 3, 1); + _glayout->addWidget(new QLabel(tr("FFT Window: "), this), 4, 0); + _glayout->addWidget(_window_combobox, 4, 1); + _glayout->addWidget(new QLabel(tr("DC Ignored: "), this), 5, 0); + _glayout->addWidget(_dc_checkbox, 5, 1); + _glayout->addWidget(new QLabel(tr("Y-axis Mode: "), this), 6, 0); + _glayout->addWidget(_view_combobox, 6, 1); + _glayout->addWidget(new QLabel(tr("DBV Range: "), this), 7, 0); + _glayout->addWidget(_dbv_combobox, 7, 1); + _glayout->addWidget(_hint_label, 0, 2, 8, 1); + _layout = new QVBoxLayout(); - _layout->addLayout(_hlayout); + _layout->addLayout(_glayout); _layout->addWidget(&_button_box); layout()->addLayout(_layout); diff --git a/DSView/pv/dialogs/fftoptions.h b/DSView/pv/dialogs/fftoptions.h index 9dcb20e4..b16dfe60 100644 --- a/DSView/pv/dialogs/fftoptions.h +++ b/DSView/pv/dialogs/fftoptions.h @@ -75,8 +75,7 @@ private: QComboBox *_dbv_combobox; QLabel *_hint_label; - QFormLayout *_flayout; - QHBoxLayout *_hlayout; + QGridLayout *_glayout; QVBoxLayout *_layout; QDialogButtonBox _button_box; diff --git a/DSView/pv/dialogs/protocolexp.cpp b/DSView/pv/dialogs/protocolexp.cpp index 2f7ec220..11f24355 100644 --- a/DSView/pv/dialogs/protocolexp.cpp +++ b/DSView/pv/dialogs/protocolexp.cpp @@ -58,6 +58,10 @@ ProtocolExp::ProtocolExp(QWidget *parent, SigSession &session) : _format_combobox->addItem(tr("Text files (*.txt)")); _flayout = new QFormLayout(); + _flayout->setVerticalSpacing(5); + _flayout->setFormAlignment(Qt::AlignLeft); + _flayout->setLabelAlignment(Qt::AlignLeft); + _flayout->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow); _flayout->addRow(new QLabel(tr("Export Format: "), this), _format_combobox); pv::data::DecoderModel* decoder_model = _session.get_decoder_model(); diff --git a/DSView/pv/dialogs/protocollist.cpp b/DSView/pv/dialogs/protocollist.cpp index e8d4b5a8..fbc51379 100644 --- a/DSView/pv/dialogs/protocollist.cpp +++ b/DSView/pv/dialogs/protocollist.cpp @@ -63,6 +63,10 @@ ProtocolList::ProtocolList(QWidget *parent, SigSession &session) : _protocol_combobox->setCurrentIndex(index); _flayout = new QFormLayout(); + _flayout->setVerticalSpacing(5); + _flayout->setFormAlignment(Qt::AlignLeft); + _flayout->setLabelAlignment(Qt::AlignLeft); + _flayout->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow); _flayout->addRow(new QLabel(tr("Decoded Protocols: "), this), _protocol_combobox); _layout = new QVBoxLayout(); diff --git a/DSView/pv/dialogs/search.cpp b/DSView/pv/dialogs/search.cpp index 5f41fc44..1469df78 100644 --- a/DSView/pv/dialogs/search.cpp +++ b/DSView/pv/dialogs/search.cpp @@ -54,6 +54,7 @@ Search::Search(QWidget *parent, boost::shared_ptr dev_inst, QSt search_buttonBox.addButton(QDialogButtonBox::Cancel); QGridLayout *search_layout = new QGridLayout(); + search_layout->setVerticalSpacing(5); search_layout->addWidget(search_label, 1, 1); search_layout->addWidget(new QLabel(tr("Search Value: ")), 2,0, Qt::AlignRight); search_layout->addWidget(&search_lineEdit, 2, 1); diff --git a/DSView/pv/dialogs/waitingdialog.cpp b/DSView/pv/dialogs/waitingdialog.cpp index 54035be1..c069c61f 100644 --- a/DSView/pv/dialogs/waitingdialog.cpp +++ b/DSView/pv/dialogs/waitingdialog.cpp @@ -40,8 +40,8 @@ using namespace std; namespace pv { namespace dialogs { -const QString WaitingDialog::TIPS_WAIT = tr("Waiting"); -const QString WaitingDialog::TIPS_FINISHED = tr("Finished!"); +const QString WaitingDialog::TIPS_WAIT = QT_TR_NOOP("Waiting"); +const QString WaitingDialog::TIPS_FINISHED = QT_TR_NOOP("Finished!"); WaitingDialog::WaitingDialog(QWidget *parent, boost::shared_ptr dev_inst) : DSDialog(parent), diff --git a/DSView/pv/dock/dsotriggerdock.cpp b/DSView/pv/dock/dsotriggerdock.cpp index 39782c05..8f6dffbb 100644 --- a/DSView/pv/dock/dsotriggerdock.cpp +++ b/DSView/pv/dock/dsotriggerdock.cpp @@ -51,6 +51,7 @@ DsoTriggerDock::DsoTriggerDock(QWidget *parent, SigSession &session) : QScrollArea(parent), _session(session) { + this->setWidgetResizable(true); _widget = new QWidget(this); QLabel *position_label = new QLabel(tr("Trigger Position: "), _widget); @@ -127,6 +128,7 @@ DsoTriggerDock::DsoTriggerDock(QWidget *parent, SigSession &session) : QVBoxLayout *layout = new QVBoxLayout(_widget); QGridLayout *gLayout = new QGridLayout(); + gLayout->setVerticalSpacing(5); gLayout->addWidget(position_label, 0, 0); gLayout->addWidget(position_spinBox, 0, 1); gLayout->addWidget(new QLabel(tr("%"), _widget), 0, 2); @@ -156,14 +158,14 @@ DsoTriggerDock::DsoTriggerDock(QWidget *parent, SigSession &session) : gLayout->addWidget(margin_label, 15, 0); gLayout->addWidget(margin_slider, 16, 0, 1, 4); - gLayout->setColumnStretch(3, 1); + gLayout->setColumnStretch(4, 1); layout->addLayout(gLayout); layout->addStretch(1); _widget->setLayout(layout); this->setWidget(_widget); - _widget->setGeometry(0, 0, sizeHint().width(), 500); + //_widget->setGeometry(0, 0, sizeHint().width(), 500); _widget->setObjectName("dsoTriggerWidget"); } diff --git a/DSView/pv/dock/measuredock.cpp b/DSView/pv/dock/measuredock.cpp index 35156573..27fe3842 100644 --- a/DSView/pv/dock/measuredock.cpp +++ b/DSView/pv/dock/measuredock.cpp @@ -66,6 +66,7 @@ MeasureDock::MeasureDock(QWidget *parent, View &view, SigSession &session) : _duty_label = new QLabel("#####", _widget); _mouse_layout = new QGridLayout(); + _mouse_layout->setVerticalSpacing(5); _mouse_layout->addWidget(_fen_checkBox, 0, 0, 1, 4); _mouse_layout->addWidget(new QLabel(tr("W: "), _widget), 1, 0); _mouse_layout->addWidget(_width_label, 1, 1); @@ -97,6 +98,7 @@ MeasureDock::MeasureDock(QWidget *parent, View &view, SigSession &session) : _t3_comboBox->setSizeAdjustPolicy(QComboBox::AdjustToContents); _cursor_layout = new QGridLayout(_widget); + _cursor_layout->setVerticalSpacing(5); _cursor_layout->addWidget(new QLabel(tr("T1: "), _widget), 0, 0); _cursor_layout->addWidget(_t1_comboBox, 0, 1); _cursor_layout->addWidget(new QLabel(tr("T2: "), _widget), 1, 0); diff --git a/DSView/pv/dock/triggerdock.cpp b/DSView/pv/dock/triggerdock.cpp index bef00fd4..0256df0c 100644 --- a/DSView/pv/dock/triggerdock.cpp +++ b/DSView/pv/dock/triggerdock.cpp @@ -125,6 +125,7 @@ TriggerDock::TriggerDock(QWidget *parent, SigSession &session) : QVBoxLayout *stage_layout = new QVBoxLayout(); QGridLayout *stage_glayout = new QGridLayout(); + stage_glayout->setVerticalSpacing(5); stage_glayout->addWidget(value_exp_label, 1, 0); stage_glayout->addWidget(inv_exp_label, 1, 1); stage_glayout->addWidget(count_exp_label, 1, 2); @@ -197,6 +198,7 @@ TriggerDock::TriggerDock(QWidget *parent, SigSession &session) : QVBoxLayout *serial_layout = new QVBoxLayout(); QGridLayout *serial_glayout = new QGridLayout(); + serial_glayout->setVerticalSpacing(5); serial_glayout->addWidget(serial_value_exp_label, 1, 1, 1, 3); serial_glayout->addWidget(_serial_start_label, 2, 0); serial_glayout->addWidget(_serial_start_lineEdit, 2, 1, 1, 3); @@ -243,6 +245,7 @@ TriggerDock::TriggerDock(QWidget *parent, SigSession &session) : QVBoxLayout *layout = new QVBoxLayout(_widget); QGridLayout *gLayout = new QGridLayout(); + gLayout->setVerticalSpacing(5); gLayout->addWidget(simple_radioButton, 0, 0); gLayout->addWidget(adv_radioButton, 1, 0); gLayout->addWidget(position_label, 2, 0); diff --git a/DSView/pv/mainwindow.cpp b/DSView/pv/mainwindow.cpp index 9b587840..af8166e4 100644 --- a/DSView/pv/mainwindow.cpp +++ b/DSView/pv/mainwindow.cpp @@ -254,6 +254,8 @@ void MainWindow::setup_ui() SLOT(malloc_error())); connect(&_session, SIGNAL(hardware_connect_failed()), this, SLOT(hardware_connect_failed())); + connect(&_session, SIGNAL(on_mode_change()), this, + SLOT(session_save())); connect(_view, SIGNAL(cursor_update()), _measure_widget, SLOT(cursor_update())); @@ -295,7 +297,7 @@ void MainWindow::update_device_list() assert(_sampling_bar); _session.stop_capture(); - _view->show_trig_cursor(false); + _view->reload(); _trigger_widget->device_change(); #ifdef ENABLE_DECODE _protocol_widget->del_all_protocol(); @@ -347,6 +349,11 @@ void MainWindow::reload() _session.reload(); } +void MainWindow::mode_changed() +{ + update_device_list(); +} + void MainWindow::load_file(QString file_name) { try { @@ -750,6 +757,7 @@ bool MainWindow::load_session(QString name) dsoSig->load_settings(); dsoSig->set_zero_vrate(obj["zeroPos"].toDouble()); dsoSig->set_trig_vrate(obj["trigValue"].toDouble()); + dsoSig->commit_settings(); } break; } diff --git a/DSView/pv/mainwindow.h b/DSView/pv/mainwindow.h index f6cdb0b6..a8e73564 100644 --- a/DSView/pv/mainwindow.h +++ b/DSView/pv/mainwindow.h @@ -76,8 +76,6 @@ public: const char *open_file_name = NULL, QWidget *parent = 0); - void session_save(); - protected: void closeEvent(QCloseEvent *event); @@ -88,6 +86,9 @@ private: bool eventFilter(QObject *object, QEvent *event); +public slots: + void session_save(); + private slots: void load_file(QString file_name); @@ -99,6 +100,8 @@ private slots: */ void update_device_list(); + void mode_changed(); + void reload(); void show_session_error( diff --git a/DSView/pv/prop/binding/binding.cpp b/DSView/pv/prop/binding/binding.cpp index 2470ba21..95fe443f 100644 --- a/DSView/pv/prop/binding/binding.cpp +++ b/DSView/pv/prop/binding/binding.cpp @@ -70,6 +70,10 @@ QWidget* Binding::get_property_form(QWidget *parent, { QWidget *const form = new QWidget(parent); QFormLayout *const layout = new QFormLayout(form); + layout->setVerticalSpacing(5); + layout->setFormAlignment(Qt::AlignLeft); + layout->setLabelAlignment(Qt::AlignLeft); + layout->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow); form->setLayout(layout); add_properties_to_form(layout, auto_commit); return form; diff --git a/DSView/pv/sigsession.cpp b/DSView/pv/sigsession.cpp index d85341fa..a42b50e0 100644 --- a/DSView/pv/sigsession.cpp +++ b/DSView/pv/sigsession.cpp @@ -473,7 +473,7 @@ double SigSession::cur_sampletime() const if (_cur_samplerate == 0) return 0; else - return _cur_samplelimits * 1.0 / _cur_samplerate; + return cur_samplelimits() * 1.0 / cur_samplerate(); } void SigSession::set_cur_samplerate(uint64_t samplerate) @@ -602,8 +602,6 @@ void SigSession::start_capture(bool instant, void SigSession::stop_capture() { _instant = false; - //_data_lock = true; - //_view_timer.stop(); #ifdef ENABLE_DECODE for (vector< boost::shared_ptr >::iterator i = _decode_traces.begin(); diff --git a/DSView/pv/sigsession.h b/DSView/pv/sigsession.h index 569ae288..59cf3327 100644 --- a/DSView/pv/sigsession.h +++ b/DSView/pv/sigsession.h @@ -333,6 +333,8 @@ signals: void show_wait_trigger(); + void on_mode_change(); + public slots: void reload(); void refresh(int holdtime); diff --git a/DSView/pv/toolbars/trigbar.cpp b/DSView/pv/toolbars/trigbar.cpp index 5a938e29..d6160005 100644 --- a/DSView/pv/toolbars/trigbar.cpp +++ b/DSView/pv/toolbars/trigbar.cpp @@ -184,6 +184,7 @@ void TrigBar::reload() _search_action->setVisible(false); _math_action->setVisible(true); } + enable_toggle(true); update(); } diff --git a/DSView/pv/view/analogsignal.cpp b/DSView/pv/view/analogsignal.cpp index e026b33e..8600d00b 100644 --- a/DSView/pv/view/analogsignal.cpp +++ b/DSView/pv/view/analogsignal.cpp @@ -57,6 +57,7 @@ AnalogSignal::AnalogSignal(boost::shared_ptr dev_inst, Signal(dev_inst, probe), _data(data) { + _typeWidth = 2; _colour = SignalColours[probe->index % countof(SignalColours)]; _scale = _totalHeight * 1.0f / 65536; } diff --git a/DSView/pv/view/decodetrace.cpp b/DSView/pv/view/decodetrace.cpp index 7a9e72ba..d1bfe666 100644 --- a/DSView/pv/view/decodetrace.cpp +++ b/DSView/pv/view/decodetrace.cpp @@ -112,8 +112,8 @@ const QColor DecodeTrace::OutlineColours[16] = { QColor(0x6B, 0x23, 0x37) }; -const QString DecodeTrace::RegionStart = "Start"; -const QString DecodeTrace::RegionEnd = "End "; +const QString DecodeTrace::RegionStart = QT_TR_NOOP("Start"); +const QString DecodeTrace::RegionEnd = QT_TR_NOOP("End "); DecodeTrace::DecodeTrace(pv::SigSession &session, boost::shared_ptr decoder_stack, int index) : @@ -377,6 +377,10 @@ void DecodeTrace::create_popup_form() _popup->reload(false); _popup_form = new QFormLayout(); + _popup_form->setVerticalSpacing(5); + _popup_form->setFormAlignment(Qt::AlignLeft); + _popup_form->setLabelAlignment(Qt::AlignLeft); + _popup_form->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow); _popup->layout()->addLayout(_popup_form); _popup->setTitle(tr("Decoder Options")); @@ -662,6 +666,11 @@ void DecodeTrace::create_decoder_form( this, SLOT(on_del_stack(boost::shared_ptr&))); QFormLayout *const decoder_form = new QFormLayout(); + decoder_form->setContentsMargins(0,0,0,0); + decoder_form->setVerticalSpacing(5); + decoder_form->setFormAlignment(Qt::AlignLeft); + decoder_form->setLabelAlignment(Qt::AlignLeft); + decoder_form->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow); group->add_layout(decoder_form); // Add the mandatory channels diff --git a/DSView/pv/view/devmode.cpp b/DSView/pv/view/devmode.cpp index 54d52ffb..6f81f27a 100644 --- a/DSView/pv/view/devmode.cpp +++ b/DSView/pv/view/devmode.cpp @@ -75,6 +75,7 @@ void DevMode::set_device() QPushButton *mode_button = new QPushButton(this); //mode_button->setFlat(true); + mode_button->setMinimumWidth(32); mode_button->setText(mode->name); mode_button->setCheckable(true); @@ -116,6 +117,7 @@ void DevMode::on_mode_change() if ((*i).first == button) { if (dev_inst->dev_inst()->mode != (*i).second->mode) { _session.stop_capture(); + _session.on_mode_change(); dev_inst->set_config(NULL, NULL, SR_CONF_DEVICE_MODE, g_variant_new_int16((*i).second->mode)); diff --git a/DSView/pv/view/dsosignal.cpp b/DSView/pv/view/dsosignal.cpp index 10b0a128..461aafc9 100644 --- a/DSView/pv/view/dsosignal.cpp +++ b/DSView/pv/view/dsosignal.cpp @@ -437,7 +437,7 @@ bool DsoSignal::load_settings() // qDebug() << "ERROR: config_get SR_CONF_EN_CH failed."; // return false; //} - gvar = _dev_inst->get_config(_probe, NULL, SR_CONF_DSO_BITS); + gvar = _dev_inst->get_config(NULL, NULL, SR_CONF_DSO_BITS); if (gvar != NULL) { _bits = g_variant_get_byte(gvar); g_variant_unref(gvar); @@ -564,6 +564,10 @@ int DsoSignal::commit_settings() ret = _dev_inst->set_config(_probe, NULL, SR_CONF_VPOS, g_variant_new_double(vpos_off)); + // -- trig_value + _dev_inst->set_config(_probe, NULL, SR_CONF_TRIGGER_VALUE, + g_variant_new_byte(_trig_value)); + return ret; } diff --git a/DSView/pv/view/ruler.cpp b/DSView/pv/view/ruler.cpp index f6050a73..c14af957 100644 --- a/DSView/pv/view/ruler.cpp +++ b/DSView/pv/view/ruler.cpp @@ -426,14 +426,15 @@ void Ruler::draw_logic_tick_mark(QPainter &p) const double SpacingIncrement = 32.0; const double MinValueSpacing = 16.0; const int ValueMargin = 5; - const double abs_min_period = 10.0 / _view.session().cur_samplerate(); double min_width = SpacingIncrement; double typical_width; double tick_period = 0; + double scale = _view.scale(); + double offset = _view.offset(); - const uint64_t cur_period_scale = ceil((_view.scale() * min_width) / abs_min_period); + const uint64_t cur_period_scale = ceil((scale * min_width) / abs_min_period); // Find tick spacing, and number formatting that does not cause // value to collide. @@ -442,19 +443,19 @@ void Ruler::draw_logic_tick_mark(QPainter &p) } else { _min_period = cur_period_scale * abs_min_period; } - const int order = (int)floorf(log10f(_view.scale() * _view.get_view_width())); + const int order = (int)floorf(log10f(scale * _view.get_view_width())); //const double order_decimal = pow(10, order); const unsigned int prefix = (order - FirstSIPrefixPower) / 3; _cur_prefix = prefix; assert(prefix < countof(SIPrefixes)); typical_width = p.boundingRect(0, 0, INT_MAX, INT_MAX, - AlignLeft | AlignTop, format_time(_view.offset(), + AlignLeft | AlignTop, format_time(offset, prefix)).width() + MinValueSpacing; do { tick_period += _min_period; - } while(typical_width > tick_period / _view.scale()); + } while(typical_width > tick_period / scale); const int text_height = p.boundingRect(0, 0, INT_MAX, INT_MAX, AlignLeft | AlignTop, "8").height(); @@ -469,9 +470,9 @@ void Ruler::draw_logic_tick_mark(QPainter &p) assert(minor_prefix < countof(SIPrefixes)); const double first_major_division = - floor(_view.offset() / tick_period); + floor(offset / tick_period); const double first_minor_division = - floor(_view.offset() / minor_tick_period + 1); + floor(offset / minor_tick_period + 1); const double t0 = first_major_division * tick_period; int division = (int)round(first_minor_division - @@ -491,7 +492,7 @@ void Ruler::draw_logic_tick_mark(QPainter &p) const double t = t0 + division * minor_tick_period; const double major_t = t0 + floor(division / MinPeriodScale) * tick_period; - x = (t - _view.offset()) / _view.scale(); + x = (t - offset) / scale; if (division % MinPeriodScale == 0) { @@ -505,13 +506,13 @@ void Ruler::draw_logic_tick_mark(QPainter &p) else { // Draw a minor tick - if (minor_tick_period / _view.scale() > 2 * typical_width) + if (minor_tick_period / scale > 2 * typical_width) p.drawText(x, 2 * ValueMargin, 0, text_height, AlignCenter | AlignTop | TextDontClip, format_time(t, prefix)); - //else if ((tick_period / _view.scale() > width() / 4) && (minor_tick_period / _view.scale() > inc_text_width)) - else if (minor_tick_period / _view.scale() > 1.1 * inc_text_width || - tick_period / _view.scale() > _view.get_view_width()) + //else if ((tick_period / scale > width() / 4) && (minor_tick_period / scale > inc_text_width)) + else if (minor_tick_period / scale > 1.1 * inc_text_width || + tick_period / scale > _view.get_view_width()) p.drawText(x, 2 * ValueMargin, 0, minor_tick_y1 + ValueMargin, AlignCenter | AlignTop | TextDontClip, format_time(t - major_t, minor_prefix)); diff --git a/DSView/pv/view/trace.cpp b/DSView/pv/view/trace.cpp index 7c923081..aec3700b 100644 --- a/DSView/pv/view/trace.cpp +++ b/DSView/pv/view/trace.cpp @@ -83,7 +83,8 @@ Trace::Trace(QString name, uint16_t index, int type) : _v_offset(INT_MAX), _type(type), _sec_index(0), - _totalHeight(30) + _totalHeight(30), + _typeWidth(SquareNum) { _index_list.push_back(index); } @@ -95,7 +96,8 @@ Trace::Trace(QString name, std::list index_list, int type, int sec_index) : _type(type), _index_list(index_list), _sec_index(sec_index), - _totalHeight(30) + _totalHeight(30), + _typeWidth(SquareNum) { } @@ -109,6 +111,7 @@ Trace::Trace(const Trace &t) : _sec_index(t._sec_index), _old_v_offset(t._old_v_offset), _totalHeight(t._totalHeight), + _typeWidth(t._typeWidth), _text_size(t._text_size) { } @@ -404,7 +407,7 @@ int Trace::get_leftWidth() const int Trace::get_rightWidth() const { - return 2 * Margin + SquareNum * SquareWidth + 1.5 * SquareWidth; + return 2 * Margin + _typeWidth * SquareWidth + 1.5 * SquareWidth; } int Trace::get_headerHeight() const diff --git a/DSView/pv/view/trace.h b/DSView/pv/view/trace.h index 04260258..1f3a4c41 100644 --- a/DSView/pv/view/trace.h +++ b/DSView/pv/view/trace.h @@ -306,6 +306,7 @@ protected: int _sec_index; int _old_v_offset; int _totalHeight; + int _typeWidth; QSizeF _text_size; }; diff --git a/DSView/pv/view/view.cpp b/DSView/pv/view/view.cpp index 357fdb8e..4a6f0d2e 100644 --- a/DSView/pv/view/view.cpp +++ b/DSView/pv/view/view.cpp @@ -167,7 +167,7 @@ View::View(SigSession &session, pv::toolbars::SamplingBar *sampling_bar, QWidget _time_viewport, SLOT(show_wait_trigger())); connect(_devmode, SIGNAL(mode_changed()), - parent, SLOT(update_device_list()), Qt::DirectConnection); + parent, SLOT(mode_changed()), Qt::DirectConnection); connect(_header, SIGNAL(traces_moved()), this, SLOT(on_traces_moved())); @@ -571,11 +571,13 @@ void View::update_scale_offset() if (_session.get_device()->dev_inst()->mode != DSO) { //_scale = (1.0 / sample_rate) / WellPixelsPerSample; _maxscale = _session.cur_sampletime() / (get_view_width() * MaxViewRate); + _minscale = (1.0 / sample_rate) / MaxPixelsPerSample; } else { _scale = _session.get_device()->get_time_base() * 10.0 / get_view_width() * std::pow(10.0, -9.0); _maxscale = 1e9; + _minscale = 1e-15; } - _minscale = (1.0 / sample_rate) / MaxPixelsPerSample; + _scale = max(min(_scale, _maxscale), _minscale); _offset = max(min(_offset, get_max_offset()), get_min_offset()); @@ -595,11 +597,7 @@ void View::signals_changed() uint8_t max_height = MaxHeightUnit; vector< boost::shared_ptr > time_traces; vector< boost::shared_ptr > fft_traces; - - if (_session.get_device()->dev_inst()->mode == LOGIC) - _viewbottom->setFixedHeight(StatusHeight); - else - _viewbottom->setFixedHeight(10); + int bits = 8; BOOST_FOREACH(const boost::shared_ptr t, get_traces(ALL_VIEW)) { if (_trace_view_map[t->get_type()] == TIME_VIEW) @@ -672,8 +670,14 @@ void View::signals_changed() next_v_offset += traceHeight + 2 * SignalMargin; boost::shared_ptr dsoSig; - if (dsoSig = dynamic_pointer_cast(t)) - dsoSig->set_scale(dsoSig->get_view_rect().height() / 256.0f); + if (dsoSig = dynamic_pointer_cast(t)) { + GVariant *gvar = _session.get_device()->get_config(NULL, NULL, SR_CONF_DSO_BITS); + if (gvar != NULL) { + bits = g_variant_get_byte(gvar); + g_variant_unref(gvar); + } + dsoSig->set_scale(dsoSig->get_view_rect().height() * 1.0f / (1 << bits)); + } } _time_viewport->clear_measure(); } @@ -1066,5 +1070,21 @@ void View::splitterMoved(int pos, int index) signals_changed(); } +void View::reload() +{ + show_trig_cursor(false); + + /* + * if headerwidth not change, viewport height will not be updated + * lead to a wrong signal height + */ + if (_session.get_device()->dev_inst()->mode == LOGIC) + _viewbottom->setFixedHeight(StatusHeight); + else + _viewbottom->setFixedHeight(10); + + +} + } // namespace view } // namespace pv diff --git a/DSView/pv/view/view.h b/DSView/pv/view/view.h index 5ba901e9..4132257c 100644 --- a/DSView/pv/view/view.h +++ b/DSView/pv/view/view.h @@ -223,6 +223,7 @@ private: void resizeEvent(QResizeEvent *e); public slots: + void reload(); void set_measure_en(int enable); void signals_changed(); void data_updated(); diff --git a/DSView/pv/view/viewport.cpp b/DSView/pv/view/viewport.cpp index a1e47c57..92fdaae1 100644 --- a/DSView/pv/view/viewport.cpp +++ b/DSView/pv/view/viewport.cpp @@ -242,18 +242,18 @@ void Viewport::paintSignals(QPainter &p) if (_view.session().get_device()->dev_inst()->mode == DSO && _view.session().get_capture_state() == SigSession::Running) { uint8_t type; - bool stream = false; + bool roll = false; QString type_str=""; - GVariant *gvar = _view.session().get_device()->get_config(NULL, NULL, SR_CONF_STREAM); + GVariant *gvar = _view.session().get_device()->get_config(NULL, NULL, SR_CONF_ROLL); if (gvar != NULL) { - stream = g_variant_get_boolean(gvar); + roll = g_variant_get_boolean(gvar); g_variant_unref(gvar); } gvar = _view.session().get_device()->get_config(NULL, NULL, SR_CONF_TRIGGER_SOURCE); if (gvar != NULL) { type = g_variant_get_byte(gvar); g_variant_unref(gvar); - if (type == DSO_TRIGGER_AUTO && stream) { + if (type == DSO_TRIGGER_AUTO && roll) { type_str = "Auto(Roll)"; } else if (type == DSO_TRIGGER_AUTO && !_view.session().trigd()) { type_str = "Auto"; @@ -929,9 +929,9 @@ void Viewport::measure() _cur_aftX = _view.hover_point().x(); _cur_midY = logicSig->get_y() - logicSig->get_totalHeight()/2 - 5; - _em_rising = "Rising: " + QString::number(_edge_rising); - _em_falling = "Falling: " + QString::number(_edge_falling); - _em_edges = "Edges: " + QString::number(_edge_rising + _edge_falling); + _em_rising = tr("Rising: ") + QString::number(_edge_rising); + _em_falling = tr("Falling: ") + QString::number(_edge_falling); + _em_edges = tr("Edges: ") + QString::number(_edge_rising + _edge_falling); break; } diff --git a/DSView/pv/widgets/decodergroupbox.cpp b/DSView/pv/widgets/decodergroupbox.cpp index b7bf92e2..1b802e38 100644 --- a/DSView/pv/widgets/decodergroupbox.cpp +++ b/DSView/pv/widgets/decodergroupbox.cpp @@ -49,6 +49,7 @@ DecoderGroupBox::DecoderGroupBox(boost::shared_ptr &decoder_ _layout(new QGridLayout(this)) { _layout->setContentsMargins(0, 0, 0, 0); + _layout->setVerticalSpacing(5); setLayout(_layout); _layout->addWidget(new QLabel(QString("

%1

").arg(_dec->decoder()->name), this), diff --git a/DSView/res/DSCope1.def.dsc b/DSView/res/DSCope1.def.dsc index 852f890f..ea6deb45 100755 --- a/DSView/res/DSCope1.def.dsc +++ b/DSView/res/DSCope1.def.dsc @@ -1,38 +1,40 @@ -{ - "Device": "DSCope", - "DeviceMode": 1, - "Horizontal trigger position": "1", - "Operation Mode": "Normal", - "Sample count": "1048576", - "Sample rate": "100000000", - "Time base": "10000", - "Trigger hold off": "0", - "Trigger slope": "0", - "Trigger source": "0", - "channel": [ - { - "colour": "#eeb211", - "coupling": 0, - "enabled": true, - "index": 0, - "name": "0", - "trigValue": 0.5, - "type": 10001, - "vdiv": 1000, - "vfactor": 1, - "zeroPos": 0.5 - }, - { - "colour": "#009925", - "coupling": 0, - "enabled": true, - "index": 1, - "name": "1", - "trigValue": 0.5, - "type": 10001, - "vdiv": 1000, - "vfactor": 1, - "zeroPos": 0.5 - } - ] -} +{ + "Device": "DSCope", + "DeviceMode": 1, + "Horizontal trigger position": "0", + "Operation Mode": "Normal", + "Sample count": "1048576", + "Sample rate": "100000000", + "Time base": "10000", + "Trigger channel": "0", + "Trigger hold off": "0", + "Trigger margin": "8", + "Trigger slope": "0", + "Trigger source": "0", + "channel": [ + { + "colour": "#eeb211", + "coupling": 0, + "enabled": true, + "index": 0, + "name": "0", + "trigValue": 0.50196078431372548, + "type": 10001, + "vdiv": 1000, + "vfactor": 1, + "zeroPos": 0.5 + }, + { + "colour": "#009925", + "coupling": 0, + "enabled": true, + "index": 1, + "name": "1", + "trigValue": 0.50196078431372548, + "type": 10001, + "vdiv": 1000, + "vfactor": 1, + "zeroPos": 0.5 + } + ] +} diff --git a/DSView/res/DSLogic0.def.dsc b/DSView/res/DSLogic0.def.dsc index 08aea6ec..83ecadd3 100755 --- a/DSView/res/DSLogic0.def.dsc +++ b/DSView/res/DSLogic0.def.dsc @@ -8,14 +8,16 @@ "Sample count": "1048576", "Sample rate": "1000000", "Threshold Level": 1, + "Trigger channel": "0", "Trigger hold off": "0", + "Trigger margin": "8", "Trigger slope": "0", "Trigger source": "0", "Using Clock Negedge": 0, "Using External Clock": 0, "channel": [ { - "colour": "#6a6a6a", + "colour": "#969696", "enabled": true, "index": 0, "name": "0", @@ -23,7 +25,7 @@ "type": 10000 }, { - "colour": "#6a6a6a", + "colour": "#969696", "enabled": true, "index": 1, "name": "1", @@ -31,7 +33,7 @@ "type": 10000 }, { - "colour": "#6a6a6a", + "colour": "#969696", "enabled": true, "index": 2, "name": "2", @@ -39,7 +41,7 @@ "type": 10000 }, { - "colour": "#6a6a6a", + "colour": "#969696", "enabled": true, "index": 3, "name": "3", @@ -47,7 +49,7 @@ "type": 10000 }, { - "colour": "#6a6a6a", + "colour": "#969696", "enabled": true, "index": 4, "name": "4", @@ -55,7 +57,7 @@ "type": 10000 }, { - "colour": "#6a6a6a", + "colour": "#969696", "enabled": true, "index": 5, "name": "5", @@ -63,7 +65,7 @@ "type": 10000 }, { - "colour": "#6a6a6a", + "colour": "#969696", "enabled": true, "index": 6, "name": "6", @@ -71,7 +73,7 @@ "type": 10000 }, { - "colour": "#6a6a6a", + "colour": "#969696", "enabled": true, "index": 7, "name": "7", @@ -79,7 +81,7 @@ "type": 10000 }, { - "colour": "#6a6a6a", + "colour": "#969696", "enabled": true, "index": 8, "name": "8", @@ -87,7 +89,7 @@ "type": 10000 }, { - "colour": "#6a6a6a", + "colour": "#969696", "enabled": true, "index": 9, "name": "9", @@ -95,7 +97,7 @@ "type": 10000 }, { - "colour": "#6a6a6a", + "colour": "#969696", "enabled": true, "index": 10, "name": "10", @@ -103,7 +105,7 @@ "type": 10000 }, { - "colour": "#6a6a6a", + "colour": "#969696", "enabled": true, "index": 11, "name": "11", @@ -111,7 +113,7 @@ "type": 10000 }, { - "colour": "#6a6a6a", + "colour": "#969696", "enabled": true, "index": 12, "name": "12", @@ -119,7 +121,7 @@ "type": 10000 }, { - "colour": "#6a6a6a", + "colour": "#969696", "enabled": true, "index": 13, "name": "13", @@ -127,7 +129,7 @@ "type": 10000 }, { - "colour": "#6a6a6a", + "colour": "#969696", "enabled": true, "index": 14, "name": "14", @@ -135,7 +137,7 @@ "type": 10000 }, { - "colour": "#6a6a6a", + "colour": "#969696", "enabled": true, "index": 15, "name": "15", diff --git a/DSView/res/DSLogic2.def.dsc b/DSView/res/DSLogic2.def.dsc index 37160a12..2d9155b8 100755 --- a/DSView/res/DSLogic2.def.dsc +++ b/DSView/res/DSLogic2.def.dsc @@ -1,81 +1,82 @@ -{ - "Channel Mode": "Use Channels 0~15 (Max 100MHz)", - "Device": "DSLogic", - "DeviceMode": 2, - "Filter Targets": "None", - "Horizontal trigger position": "245", - "Operation Mode": "Buffer Mode", - "Sample count": "1048576", - "Sample rate": "100000000", - "Threshold Level": "1.8/2.5/3.3V Level", - "Trigger hold off": "0", - "Trigger slope": "0", - "Trigger source": "0", - "Using Clock Negedge": 0, - "Using External Clock": 0, - "channel": [ - { - "colour": "#1185d1", - "enabled": true, - "index": 0, - "name": "0", - "type": 10002 - }, - { - "colour": "#eeb211", - "enabled": true, - "index": 1, - "name": "1", - "type": 10002 - }, - { - "colour": "#d50f25", - "enabled": true, - "index": 2, - "name": "2", - "type": 10002 - }, - { - "colour": "#009925", - "enabled": true, - "index": 3, - "name": "3", - "type": 10002 - }, - { - "colour": "#1185d1", - "enabled": true, - "index": 4, - "name": "4", - "type": 10002 - }, - { - "colour": "#eeb211", - "enabled": true, - "index": 5, - "name": "5", - "type": 10002 - }, - { - "colour": "#d50f25", - "enabled": true, - "index": 6, - "name": "6", - "type": 10002 - }, - { - "colour": "#009925", - "enabled": true, - "index": 7, - "name": "7", - "type": 10002 - }, - { - "colour": "#1185d1", - "enabled": true, - "index": 8, - "name": "8", - "type": 10002 - } - ] -} +{ + "Channel Mode": "Use Channels 0~15 (Max 10MHz)", + "Device": "DSLogic", + "DeviceMode": 2, + "Filter Targets": "None", + "Horizontal trigger position": "0", + "Operation Mode": "Stream Mode", + "Sample count": "1048576", + "Sample rate": "1000000", + "Threshold Level": "1.8/2.5/3.3V Level", + "Trigger hold off": "0", + "Trigger margin": "8", + "Trigger slope": "0", + "Trigger source": "0", + "Using Clock Negedge": 0, + "Using External Clock": 0, + "channel": [ + { + "colour": "#1185d1", + "enabled": true, + "index": 0, + "name": "0", + "type": 10002 + }, + { + "colour": "#eeb211", + "enabled": true, + "index": 1, + "name": "1", + "type": 10002 + }, + { + "colour": "#d50f25", + "enabled": true, + "index": 2, + "name": "2", + "type": 10002 + }, + { + "colour": "#009925", + "enabled": true, + "index": 3, + "name": "3", + "type": 10002 + }, + { + "colour": "#1185d1", + "enabled": true, + "index": 4, + "name": "4", + "type": 10002 + }, + { + "colour": "#eeb211", + "enabled": true, + "index": 5, + "name": "5", + "type": 10002 + }, + { + "colour": "#d50f25", + "enabled": true, + "index": 6, + "name": "6", + "type": 10002 + }, + { + "colour": "#009925", + "enabled": true, + "index": 7, + "name": "7", + "type": 10002 + }, + { + "colour": "#1185d1", + "enabled": true, + "index": 8, + "name": "8", + "type": 10002 + } + ] +} diff --git a/libsigrok4DSL/hardware/DSL/dscope.c b/libsigrok4DSL/hardware/DSL/dscope.c index b355ebb5..bd7481ae 100644 --- a/libsigrok4DSL/hardware/DSL/dscope.c +++ b/libsigrok4DSL/hardware/DSL/dscope.c @@ -1276,11 +1276,11 @@ static int config_get(int id, GVariant **data, const struct sr_dev_inst *sdi, devc = sdi->priv; *data = g_variant_new_boolean(devc->cali); break; - case SR_CONF_STREAM: + case SR_CONF_ROLL: if (!sdi) return SR_ERR; devc = sdi->priv; - *data = g_variant_new_boolean(devc->stream); + *data = g_variant_new_boolean(devc->roll); break; case SR_CONF_TEST: if (!sdi) @@ -2408,7 +2408,7 @@ static void receive_transfer(struct libusb_transfer *transfer) mstatus.vlen != 0 && mstatus.vlen <= (transfer->actual_length - 512) / sample_width) || devc->instant) { - devc->stream = (mstatus.stream_mode != 0); + devc->roll = (mstatus.stream_mode != 0); devc->mstatus_valid = TRUE; packet.type = SR_DF_DSO; packet.payload = &dso; diff --git a/libsigrok4DSL/hardware/DSL/dsl.h b/libsigrok4DSL/hardware/DSL/dsl.h index 6fa3061b..4916367f 100644 --- a/libsigrok4DSL/hardware/DSL/dsl.h +++ b/libsigrok4DSL/hardware/DSL/dsl.h @@ -250,6 +250,7 @@ struct DSL_context { int zero_pcnt; int zero_comb; gboolean stream; + gboolean roll; gboolean data_lock; uint8_t dso_bits; diff --git a/libsigrok4DSL/hardware/DSL/dslogic.c b/libsigrok4DSL/hardware/DSL/dslogic.c index 503ecbf8..973a2667 100644 --- a/libsigrok4DSL/hardware/DSL/dslogic.c +++ b/libsigrok4DSL/hardware/DSL/dslogic.c @@ -134,12 +134,12 @@ static const int32_t hwoptions_pro[] = { }; static const int32_t sessions[] = { + SR_CONF_OPERATION_MODE, + SR_CONF_CHANNEL_MODE, SR_CONF_SAMPLERATE, SR_CONF_LIMIT_SAMPLES, SR_CONF_CLOCK_TYPE, SR_CONF_CLOCK_EDGE, - SR_CONF_OPERATION_MODE, - SR_CONF_CHANNEL_MODE, SR_CONF_THRESHOLD, SR_CONF_FILTER, SR_CONF_TRIGGER_SLOPE, @@ -150,12 +150,12 @@ static const int32_t sessions[] = { }; static const int32_t sessions_pro[] = { + SR_CONF_OPERATION_MODE, + SR_CONF_CHANNEL_MODE, SR_CONF_SAMPLERATE, SR_CONF_LIMIT_SAMPLES, SR_CONF_CLOCK_TYPE, SR_CONF_CLOCK_EDGE, - SR_CONF_OPERATION_MODE, - SR_CONF_CHANNEL_MODE, SR_CONF_VTH, SR_CONF_FILTER, SR_CONF_TRIGGER_SLOPE, @@ -937,6 +937,7 @@ static uint64_t dso_cmd_gen(const struct sr_dev_inst *sdi, struct sr_channel* ch uint64_t cmd = 0; int channel_cnt = 0; GSList *l; + struct sr_channel *en_probe; devc = sdi->priv; @@ -947,14 +948,18 @@ static uint64_t dso_cmd_gen(const struct sr_dev_inst *sdi, struct sr_channel* ch case SR_CONF_COUPLING: for (l = sdi->channels; l; l = l->next) { struct sr_channel *probe = (struct sr_channel *)l->data; - if (probe->enabled) + if (probe->enabled) { channel_cnt += probe->index + 0x1; + en_probe = probe; + } } if (channel_cnt == 0) return 0x0; // --VDBS - switch(ch->vdiv){ + if (channel_cnt != 1) + en_probe = ch; + switch(en_probe->vdiv){ case 5: cmd += 0x247000; break; case 10: cmd += 0x23D000; break; case 20: cmd += 0x22F000; break; @@ -1317,6 +1322,12 @@ static int config_get(int id, GVariant **data, const struct sr_dev_inst *sdi, devc = sdi->priv; *data = g_variant_new_boolean(devc->stream); break; + case SR_CONF_ROLL: + if (!sdi) + return SR_ERR; + devc = sdi->priv; + *data = g_variant_new_boolean(devc->roll); + break; case SR_CONF_MAX_DSO_SAMPLERATE: if (!sdi) return SR_ERR; @@ -1410,7 +1421,6 @@ static int config_set(int id, GVariant *data, struct sr_dev_inst *sdi, num_probes = devc->profile->dev_caps & DEV_CAPS_16BIT ? 16 : 8; devc->samplecounts_size = ARRAY_SIZE(samplecounts); } else if (sdi->mode == DSO) { - sdi->mode = DSO; num_probes = devc->profile->dev_caps & DEV_CAPS_16BIT ? MAX_DSO_PROBES_NUM : 1; ret = command_dso_ctrl(usb->devhdl, dso_cmd_gen(sdi, NULL, SR_CONF_DSO_SYNC)); if (ret != SR_OK) @@ -1558,8 +1568,13 @@ static int config_set(int id, GVariant *data, struct sr_dev_inst *sdi, __func__, devc->th_level); } else if (id == SR_CONF_VTH) { devc->vth = g_variant_get_double(data); - sr_dbg("%s: setting threshold voltage to %f failed", - __func__, devc->vth); + if ((ret = command_wr_reg(usb->devhdl, (uint8_t)(devc->vth/5.0*255), VTH_ADDR)) == SR_OK) { + sr_err("%s: setting threshold voltage to %f", + __func__, devc->vth); + } else { + sr_info("%s: setting threshold voltage to %f failed", + __func__, devc->vth); + } } else if (id == SR_CONF_FILTER) { stropt = g_variant_get_string(data, NULL); if (!strcmp(stropt, filters[SR_FILTER_NONE])) { @@ -1889,6 +1904,14 @@ static int dev_open(struct sr_dev_inst *sdi) sr_err("%s: Configure FPGA failed!", __func__); } g_free(fpga_bit); + + if ((ret = command_wr_reg(usb->devhdl, (uint8_t)(devc->vth/5.0*255), VTH_ADDR)) == SR_OK) { + sr_err("%s: setting threshold voltage to %f", + __func__, devc->vth); + } else { + sr_info("%s: setting threshold voltage to %f failed", + __func__, devc->vth); + } } } @@ -2144,7 +2167,7 @@ static void receive_transfer(struct libusb_transfer *transfer) mstatus.vlen != 0 && mstatus.vlen <= (transfer->actual_length - 512) / sample_width) || devc->instant) { - devc->stream = (mstatus.stream_mode != 0); + devc->roll = (mstatus.stream_mode != 0); devc->mstatus_valid = TRUE; packet.type = SR_DF_DSO; packet.payload = &dso; @@ -2491,14 +2514,6 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi, void *cb_data) else sr_dbg("%s: setting DSO Horiz Trigger Position to %d failed", __func__, devc->trigger_hpos); - } else if (sdi->mode == LOGIC) { - if ((ret = command_wr_reg(usb->devhdl, (uint8_t)(devc->vth/5.0*255), VTH_ADDR)) == SR_OK) { - sr_err("%s: setting threshold voltage to %f", - __func__, devc->vth); - } else { - sr_info("%s: setting threshold voltage to %f failed", - __func__, devc->vth); - } } /* poll trigger status transfer*/ diff --git a/libsigrok4DSL/hardware/demo/demo.c b/libsigrok4DSL/hardware/demo/demo.c index 6c279323..f575db7f 100644 --- a/libsigrok4DSL/hardware/demo/demo.c +++ b/libsigrok4DSL/hardware/demo/demo.c @@ -146,7 +146,7 @@ static const int32_t sessions[] = { SR_CONF_PATTERN_MODE, }; -static const int const_dc = 50; +static const int const_dc = 1.95 / 10 * 255; static const int sinx[] = { 0, 2, 3, 5, 6, 8, 9, 11, 12, 14, 16, 17, 18, 20, 21, 23, 24, 26, 27, 28, 30, 31, 32, 33, 34, 35, 37, 38, 39, 40, 41, 41, 42, 43, 44, 45, 45, 46, 47, 47, @@ -357,7 +357,7 @@ static GSList *hw_scan(GSList *options) devc->limit_samples_show = devc->limit_samples; devc->limit_msec = 0; devc->sample_generator = PATTERN_SINE; - devc->timebase = 200; + devc->timebase = 500; devc->data_lock = FALSE; devc->max_height = 0; devc->dso_bits = 8; @@ -876,7 +876,8 @@ static void samples_generator(uint16_t *buf, uint64_t size, for (l = sdi->channels; l; l = l->next) { start_rand = devc->pre_index == 0 ? rand()%len : 0; probe = (struct sr_channel *)l->data; - offset = ceil((0.5 - (probe->vpos/probe->vdiv/10.0)) * 255); + //offset = ceil((0.5 - (probe->vpos/probe->vdiv/10.0)) * 255); + offset = 128; pre0_i = devc->pre_index; pre1_i = devc->pre_index; for (i = devc->pre_index; i < devc->pre_index + size; i++) { diff --git a/libsigrok4DSL/libsigrok.h b/libsigrok4DSL/libsigrok.h index 7626c5de..c40428bb 100644 --- a/libsigrok4DSL/libsigrok.h +++ b/libsigrok4DSL/libsigrok.h @@ -785,6 +785,9 @@ enum { /** Stream */ SR_CONF_STREAM, + /** DSO Roll */ + SR_CONF_ROLL, + /** Test */ SR_CONF_TEST, From b15bf268436ccdf10c45943500c621a59b64765f Mon Sep 17 00:00:00 2001 From: DreamSourceLab Date: Wed, 10 Aug 2016 16:45:36 +0800 Subject: [PATCH 30/32] Bumped version number to 0.96 --- DSView/CMakeLists.txt | 19 +++---------------- DSView/pv/view/analogsignal.cpp | 2 +- 2 files changed, 4 insertions(+), 17 deletions(-) diff --git a/DSView/CMakeLists.txt b/DSView/CMakeLists.txt index c8afe9df..5df84709 100644 --- a/DSView/CMakeLists.txt +++ b/DSView/CMakeLists.txt @@ -104,7 +104,7 @@ set(DS_DESCRIPTION "A GUI for instruments of DreamSourceLab") set(DS_VERSION_MAJOR 0) set(DS_VERSION_MINOR 9) -set(DS_VERSION_MICRO 5) +set(DS_VERSION_MICRO 6) set(DS_VERSION_STRING ${DS_VERSION_MAJOR}.${DS_VERSION_MINOR}.${DS_VERSION_MICRO} ) @@ -394,21 +394,8 @@ set_target_properties(${PROJECT_NAME} PROPERTIES INSTALL_RPATH "/usr/local/lib") # Install the executable. install(TARGETS ${PROJECT_NAME} DESTINATION bin/) -install(FILES res/DSLogic.fw DESTINATION bin/res/) -install(FILES res/DSLogic33.bin DESTINATION bin/res/) -install(FILES res/DSLogic50.bin DESTINATION bin/res/) -install(FILES res/DSLogicPro.fw DESTINATION bin/res/) -install(FILES res/DSLogicPro.bin DESTINATION bin/res/) -install(FILES res/DSCope.fw DESTINATION bin/res/) -install(FILES res/DSCope.bin DESTINATION bin/res/) -install(FILES res/DSLogic0.dsc DESTINATION bin/res/) -install(FILES res/DSLogic0.def.dsc DESTINATION bin/res/) -install(FILES res/DSLogic1.dsc DESTINATION bin/res/) -install(FILES res/DSLogic1.def.dsc DESTINATION bin/res/) -install(FILES res/DSLogic2.dsc DESTINATION bin/res/) -install(FILES res/DSLogic2.def.dsc DESTINATION bin/res/) -install(FILES res/DSCope1.dsc DESTINATION bin/res/) -install(FILES res/DSCope1.def.dsc DESTINATION bin/res/) +install(DIRECTORY res DESTINATION share/${PROJECT_NAME}) +install(DIRECTORY ../libsigrokdecode4DSL/decoders DESTINATION share/${PROJECT_NAME}) #=============================================================================== #= Packaging (handled by CPack) diff --git a/DSView/pv/view/analogsignal.cpp b/DSView/pv/view/analogsignal.cpp index 8600d00b..08d38c2e 100644 --- a/DSView/pv/view/analogsignal.cpp +++ b/DSView/pv/view/analogsignal.cpp @@ -57,7 +57,7 @@ AnalogSignal::AnalogSignal(boost::shared_ptr dev_inst, Signal(dev_inst, probe), _data(data) { - _typeWidth = 2; + _typeWidth = 3; _colour = SignalColours[probe->index % countof(SignalColours)]; _scale = _totalHeight * 1.0f / 65536; } From fdb59518d2964d7290fca84e1b92516ebeaf2fec Mon Sep 17 00:00:00 2001 From: DreamSourceLab Date: Thu, 11 Aug 2016 12:57:35 +0800 Subject: [PATCH 31/32] Fix compile issue --- libsigrokdecode4DSL/.decoder.c.swp | Bin 16384 -> 0 bytes libsigrokdecode4DSL/.type_decoder.c.swp | Bin 16384 -> 0 bytes libsigrokdecode4DSL/libsigrokdecode.pc.in | 18 ++++++++++++++++++ 3 files changed, 18 insertions(+) delete mode 100644 libsigrokdecode4DSL/.decoder.c.swp delete mode 100644 libsigrokdecode4DSL/.type_decoder.c.swp create mode 100644 libsigrokdecode4DSL/libsigrokdecode.pc.in diff --git a/libsigrokdecode4DSL/.decoder.c.swp b/libsigrokdecode4DSL/.decoder.c.swp deleted file mode 100644 index cce209d61cafe8d080b1b77677e0217e146ec988..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16384 zcmeI3U5p!76~~7{X-sK=P$3~2da@Cf?RxD^iiFhNE^#+b*6L>0^2a7y3Cwur+MZ-Q zcQkWn9XF(+h$iI$1W2@M1P?q=!3z%vsS*N#Dp3{jDO9Q!fdqvIq=G5|LV}Wn|CtZl z`;jI{RUgn;{$)LP?#H?Rd+wR>8K*P1UOhn{DSlYP>)o36)%R%Ye=O|PezTxyo|KA< zulEW%fun-!)zY+5Uk*e`cg#^~D7`bKqm6Y-3`@4v@vWXG&zQoHrYJ>!yrOaa9@84B z0;vKwuE1^DJ%qB@n&hwRDo21RDo21RDo21RDo21RDo21{|g0_dy94z zGr0+T0lX#VDqd&t`HS)K6&!yOpEIbybuQyr;_svJ@qcjqB^>`FK7I|yPvH0hI_COs z1qAlv10ScaRDo21RDo21RDo21RDo21RDo21RDo21RDo21H%|fG)UGX;PYSutb@Ja*}aAQ2jFS&4e%8BI`|ss0t4Iy4uaqBfvwyN;3PN<4uG4%Ki{rte+Pd9e*!OqKY&Z% zF>nEV0#raAPOp^ zdc9rSXfCg<@(HU;*{nNio0g$0B?fKXZ~MyQBH<`+RG=A0ABdR(-Q}7_e|C|NaydE> zb+-0Ej?ST*H*Pe`oQ!3e+egAsYzC&%YY%kWmPXdM9ul&fh0%eb$oI^)(bpYE*ty(1 z(q}hnS|&52lWqBmhL-9R>p{X61E}B^VUih5y+FEB9(snRJeKgh>`cv*N*dCp_?I;F zA)nt|)9M6DkmZvj70sX(3x1$BI*R_=OMPLS8B2}1vXqJPm}70!3|w2Zj|)}B+HlQy zZft0QEViR7J<3|BJvUDlEszrol3G4W?!G(hcC!9!4}`LyAn=^fBe*1!Ddnj$aMg&W zhA2|Lq(we5-R+cV4XD4(F3jUTSd)YJ>=si4ZFRj&W~ZkAv!psHmIx~p*$HVQ&BoeJ zO(#-#tBT=V);gyJ)&n}1v1Sb{o72m}I#k7yj@NwaY{s&!%0x4tWS3MQ%dJMPAm0^+ z)wP5<(Gm8=gDva+>kP)~S&ky8SJ<+oSaqz{PH(3?Zw#DD20+0!cj1C?Q_pMXFqPwt z6?XT0*wD^tzS5Ot5RcjuYV1Q$@VP^}Y zL+l4=%-$Am1N3PO+$@S_7cF$g`yJaO!)?~B{ES%S`}ZJF`>qbb5djrRoQ>49Pq2e5l z!=iYO&Au3if-ssW6pWpLsz(ciP0Q*Kbl}L)m4RaxlWtty(+5z4xTES(wiON;`PQUo zCD}W$iIaA4UpU{S!mdLXwNgGmy-10uQ`$s}n#eY-3d^6}l)K4rc!vz3dtpPwlKO?I zDLaC6526IFD?KGl7Q~jiiM7ffJs`)xZ~%2ym>t(%|yRRba*Hc)B-Ag1zNf%F=Db zN3%G?ftp=@pr38~!uKudz_k#q=h%7AY4P6cb)>X~?!=cdTpilnO{_SZMn{BWVtq{Z zLJiI@;vinDSJ!dDh=L2{2hwa$|HyM5jR}PTE+^CFMPt{FI0$UDb$-WSWB-We&mhr<_oA=euUXqPyiY|U98@SZ-+$1v4_N%=gxlG(WJ3Trrgx(x}2po2{@FPYf z+?-r**sf)ZsMCXpF?L`ITIk_dyT$&ZHZGaj_A}mMf9BdJXTyEt=r(E*L_B7Bd;{T2 z8e${zhQ>Z?*VY={#g8 zLjWrhRm13paRg@2SIWJ&R2mM4#hw!srPsqRkqzO%WlD>@YiUCUG>x?@}0^sr079AIk@N}DMK?j}CO4L!jZr84WK6RTv3p!D$FD;i> zo8|kfE7j%($D(7^=4z$Upkr%wDpRdoZ&sIDE9E-XT6M@&!Q>j46oLyIj+;C>S;akd zt4Zb64LVt_*U{g`e5i`VS+OacFcet>*Tx+Ii|gqQZntEYdkCXlEF4D=6K)jUJes&7 zhb{XOw-l^5Ddi;=)niG_*^svH{+J!n(c-7jUPR*l;dX) kzL=yew?BWtx9KEX%>CmrSDfxW!1@^clrPHT;_=3R0piw^0{{R3 diff --git a/libsigrokdecode4DSL/.type_decoder.c.swp b/libsigrokdecode4DSL/.type_decoder.c.swp deleted file mode 100644 index 6ca9f4e887c9acae23b4993cdd8105bcb074fe4c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16384 zcmeHOTWlj&89u!(p%*H6K&nvtulFK5b?n^~(XyAQ-o)88n{_PP$&xP1FrGQKXLmef zX3oTQ(}kr_sg<}^An^bdP#!=+2oRSRfdmglMLh6EQ6(fmeFR?u`_GIMJIXdG zPzk|U`r?^${r~U(&+R`>>&R;596eBYr@-efA-=uuhSft4+$o+pAw=N&ij#NyM6JkE z(Qc)LD0cmxESjD@EDrqOa&e)t>c~OSby}g*3H-~pw0v6@)vzy(_&F$8yEmJbNEt{O z*b@VHi2G-c9yyTNPj7qc%ARVZg;EAm22uu622uu622uu622uu62L5*#Q2m?5HK=nV zz(@ES5?sS)9nTC-`v=~K`2IukeiObA0uq^=pMFvXQU+26QU+26QU+26QU+26QU+26 zQU+26QU?Be88Gem9Dh3p0C4}$F@RU!BE*Zp3&5v=PXJ}$)jNfF4)_f4N#HCn1-y8N z5T6G=2h@Olz)ir7z@Kgx;^0DcNw z1q|R};6dPS;N^WnJOexdJPy1ccsuaYtwMYe_!#gJ;41Ji@E~wE@bWD}d>MEWcmk*b zcLKk;QPSSz=weza1mGl=7GC_zrc3i z0X_I&k7p0sZ1m3e!6eBj*AUuVW(nfhD+x}s7&Qp;8c<2d+Rb# zNekJrdI@a?Gf&gDlc(BnBo3^V7Xl_7v*_~K<1n-C0cJrzbdvA}nelgK&h$J($ww4Y zM~~xKnuH{8LMI2)QZThU0@8#e%ySZB?#IK1wd*q`uFhfEw zZ9nKqdq>yOuDD0v+LDD_Csgow)8^^$h!#Dlc#JHc)lT=? zBWEX7Kn1*$`+h@p!6;cmA4WV#Vm)$NAf^R2a4bX2(&l?il<#$nQn-Y##3xwPU5wv$0$Lw7){D)HKQu<{7FY2+TF?cWT}hK#hVe4PuCl2oMz6BEj@N=O`a}b=7fp!R45cKUYuB7r+F;#@&ec5 zWsP5TExeDdX*(_$dRg37&Nlo=^)YQ@$@G!8we-bP+rPNVZs|0>jO}5ig$S8syK=7F zEHTrg_jvm272dVw>sGz#SFz}xT4b+^lx;7 zZe&Y30i$ttSm@4erDnGM`nS-{Tt9P$r@X*3U6djyvB*G@$#pJ+|I)T|M5LMcQDHr`n zXwg-wf2>#>3le_r>iL1h##fn+8|r?~mFx3oO4VlRRAs5sTqD?X zvC^!T8x2}qu2YF>rFye6zp_-SQ*EUVn#xeF0i{$|a)-Q!IbXqseWgjI>KdId)$3?) z?YL$IzCdzadR&^EUf*?~637iqPq9+mg7&I}IQA|Z(_i4AT@-cVnq9xo9h!<9o*7-2 zHw19NcI4(0#k0s9`cXiBU+?xX)Q9AnFg;--8)5^f^<_C01Bl=7+X{l<&5t}R=qQjU ziM{?-1-gGkd%|%Z^svMb0(DT3TSE^$3<;b+Uei8=tz@4q7IvXmYq*oc9+;2W|mg!Fm5jz|+8|0FDE!0SAEt0LKIN0k;Cb!TJArpbOj!{2gcgSAgFEzXg5) zdiTwwBYkozAEh94!1=asAukjEbM2@Mwaie)(KiksaJjZ?cDR+= z&}h~cmrAD_#zJ|qw6fHs$Ho&+l^SIZBP7+(kYjPg9#uzyd)!$hvgDFd9E;kLB94O; zk}@nnZMbR%%lZyPhY*ZPwbIO1SC*FYbR;J@NM5T~Rv||d!GpNqcqBq!C!qt5YJeuq z5mzWR%R0WyWNpOFC5`5ZA2{kdm8mB|U!2W7IY{Ci9KDccFLMRrZDdB_O9TaRIX1r- zd6qtvjyNCX=Lun(GsbGEUaFsNJf>gHme(anAz>gX8yX11=3N^vk;Hv|Dq{1{hCd;ck^o+-$NSCvGdp z5hygM+u>)UvkqA~q#4J$5_dW|etXE_IKK1XYc5t%U&dyB#g>ocIaZ~^ Date: Thu, 11 Aug 2016 13:02:28 +0800 Subject: [PATCH 32/32] Fix compile issue --- DSView/CMake/FindFFTW.cmake | 71 +++++++++++++++++++++++++++++++++++++ INSTALL | 4 +-- 2 files changed, 73 insertions(+), 2 deletions(-) create mode 100644 DSView/CMake/FindFFTW.cmake diff --git a/DSView/CMake/FindFFTW.cmake b/DSView/CMake/FindFFTW.cmake new file mode 100644 index 00000000..df9c8b38 --- /dev/null +++ b/DSView/CMake/FindFFTW.cmake @@ -0,0 +1,71 @@ +# - Find FFTW +# Find the native FFTW includes and library +# This module defines +# FFTW_INCLUDE_DIR, where to find fftw3.h, etc. +# FFTW_LIBRARIES, the libraries needed to use FFTW. +# FFTW_FOUND, If false, do not try to use FFTW. +# also defined, but not for general use are +# FFTW_LIBRARY, where to find the FFTW library. + +FIND_PATH(FFTW_INCLUDE_DIR fftw3.h +/usr/local/include +/usr/include +/opt/local/lib +) + +SET(FFTW_NAMES ${FFTW_NAMES} fftw3 fftw3f fftw3-3) +FIND_LIBRARY(FFTW_LIBRARY + NAMES ${FFTW_NAMES} + PATHS /usr/lib /usr/local/lib /opt/locala/lib + ) + +# Find threads part of FFTW + +SET(FFTW_THREADS_NAMES ${FFTW_THREADS_NAMES} fftw3f_threads fftw3_threads fftw3-3_threads) +FIND_LIBRARY(FFTW_THREADS_LIBRARY + NAMES ${FFTW_THREADS_NAMES} + PATHS /usr/lib /usr/local/lib /opt/local/lib + ) + +IF (FFTW_THREADS_LIBRARY AND FFTW_INCLUDE_DIR) + SET(FFTW_THREADS_LIBRARIES ${FFTW_THREADS_LIBRARY}) + SET(FFTW_THREADS_FOUND "YES") +ELSE (FFTW_THREADS_LIBRARY AND FFTW_INCLUDE_DIR) + SET(FFTW_THREADS_FOUND "NO") +ENDIF (FFTW_THREADS_LIBRARY AND FFTW_INCLUDE_DIR) + + +IF (FFTW_THREADS_FOUND) + IF (NOT FFTW_THREADS_FIND_QUIETLY) + MESSAGE(STATUS "Found FFTW threads: ${FFTW_THREADS_LIBRARIES}") + ENDIF (NOT FFTW_THREADS_FIND_QUIETLY) +ELSE (FFTW_THREADS_FOUND) + IF (FFTW_THREADS_FIND_REQUIRED) + MESSAGE(FATAL_ERROR "Could not find FFTW threads library") + ENDIF (FFTW_THREADS_FIND_REQUIRED) +ENDIF (FFTW_THREADS_FOUND) + + +IF (FFTW_LIBRARY AND FFTW_INCLUDE_DIR) + SET(FFTW_LIBRARIES ${FFTW_LIBRARY}) + SET(FFTW_FOUND "YES") +ELSE (FFTW_LIBRARY AND FFTW_INCLUDE_DIR) + SET(FFTW_FOUND "NO") +ENDIF (FFTW_LIBRARY AND FFTW_INCLUDE_DIR) + + +IF (FFTW_FOUND) + IF (NOT FFTW_FIND_QUIETLY) + MESSAGE(STATUS "Found FFTW: ${FFTW_LIBRARIES}") + ENDIF (NOT FFTW_FIND_QUIETLY) +ELSE (FFTW_FOUND) + IF (FFTW_FIND_REQUIRED) + MESSAGE(FATAL_ERROR "Could not find FFTW library") + ENDIF (FFTW_FIND_REQUIRED) +ENDIF (FFTW_FOUND) + +SET (ON_UNIX ${CMAKE_SYSTEM_NAME} STREQUAL "Linux" OR + ${CMAKE_SYSTEM_NAME} STREQUAL "Darwin") +IF (${ON_UNIX}) + SET (FFTW_EXECUTABLE_LIBRARIES fftw3f fftw3f_threads) +ENDIF (${ON_UNIX}) diff --git a/INSTALL b/INSTALL index 82b83232..283ff32c 100644 --- a/INSTALL +++ b/INSTALL @@ -38,9 +38,9 @@ Step2: Installing the requirements: please check your respective distro's package manager tool if you use other distros Debian/Ubuntu: - $ sudo apt-get install git-core gcc gcc-c++ make cmake autoconf automake libtool pkg-config \ + $ sudo apt-get install git-core build-essential cmake autoconf automake libtool pkg-config \ libglib2.0-dev libzip-dev libudev-dev libusb-1.0-0-dev \ - python3-dev qt5-default qt5-qtbase-devel libboost-dev libboost-test-dev libboost-thread-dev libboost-system-dev libboost-filesystem-dev check libfftw3-dev + python3-dev qt5-default libboost-dev libboost-test-dev libboost-thread-dev libboost-system-dev libboost-filesystem-dev check libfftw3-dev Fedora (18, 19): $ sudo yum install git gcc g++ make cmake autoconf automake libtool pkgconfig glib2-devel \

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 vo;v_p2ANc? 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 %D4zz2ZK~{T&I7EkS{;-`W78n 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 literal 340884 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