diff --git a/CMakeLists.txt b/CMakeLists.txt
index add9c02f..47ff8264 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -273,6 +273,7 @@ set(pulseview_SOURCES
pv/dialogs/storeprogress.cpp
pv/popups/deviceoptions.cpp
pv/popups/channels.cpp
+ pv/popups/triggermode.cpp
pv/prop/bool.cpp
pv/prop/double.cpp
pv/prop/enum.cpp
@@ -342,6 +343,7 @@ set(pulseview_HEADERS
pv/dialogs/storeprogress.hpp
pv/popups/channels.hpp
pv/popups/deviceoptions.hpp
+ pv/popups/triggermode.hpp
pv/prop/bool.hpp
pv/prop/double.hpp
pv/prop/enum.hpp
diff --git a/icons/status-yellow.svg b/icons/status-yellow.svg
new file mode 100644
index 00000000..48433542
--- /dev/null
+++ b/icons/status-yellow.svg
@@ -0,0 +1,24 @@
+
+
+
diff --git a/icons/trigger-repeat-rearm.svg b/icons/trigger-repeat-rearm.svg
new file mode 100644
index 00000000..f481f7c7
--- /dev/null
+++ b/icons/trigger-repeat-rearm.svg
@@ -0,0 +1,118 @@
+
+
+
+
diff --git a/pulseview.qrc b/pulseview.qrc
index 706fe8d0..49d72e47 100644
--- a/pulseview.qrc
+++ b/pulseview.qrc
@@ -26,6 +26,7 @@
icons/status-green.svg
icons/status-grey.svg
icons/status-red.svg
+ icons/status-yellow.svg
icons/show-cursors.svg
icons/trigger-change.svg
icons/trigger-falling.svg
@@ -36,6 +37,7 @@
icons/trigger-marker-high.svg
icons/trigger-marker-low.svg
icons/trigger-marker-rising.svg
+ icons/trigger-repeat-rearm.svg
icons/trigger-none.svg
icons/trigger-rising.svg
icons/view-displaymode-last_complete_segment.svg
diff --git a/pv/mainwindow.cpp b/pv/mainwindow.cpp
index 34938e73..d288a65a 100644
--- a/pv/mainwindow.cpp
+++ b/pv/mainwindow.cpp
@@ -74,6 +74,7 @@ MainWindow::MainWindow(DeviceManager &device_manager, QWidget *parent) :
device_manager_(device_manager),
session_selector_(this),
icon_red_(":/icons/status-red.svg"),
+ icon_yellow_(":/icons/status-yellow.svg"),
icon_green_(":/icons/status-green.svg"),
icon_grey_(":/icons/status-grey.svg")
{
@@ -583,7 +584,8 @@ void MainWindow::update_acq_button(Session *session)
run_caption = tr("Run");
}
- const QIcon *icons[] = {&icon_grey_, &icon_red_, &icon_green_};
+ // Maps to pv::Session::capture_state enum elements
+ const QIcon *icons[] = {&icon_grey_, &icon_grey_, &icon_red_, &icon_green_, &icon_yellow_};
run_stop_button_->setIcon(*icons[state]);
run_stop_button_->setText((state == pv::Session::Stopped) ?
run_caption : tr("Stop"));
@@ -681,6 +683,7 @@ void MainWindow::on_run_stop_clicked()
bool any_running = any_of(hw_sessions.begin(), hw_sessions.end(),
[](const shared_ptr &s)
{ return (s->get_capture_state() == Session::AwaitingTrigger) ||
+ (s->get_capture_state() == Session::AwaitingRearm) ||
(s->get_capture_state() == Session::Running); });
for (shared_ptr s : hw_sessions)
@@ -701,6 +704,8 @@ void MainWindow::on_run_stop_clicked()
session->start_capture([&](QString message) {
show_session_error("Capture failed", message); });
break;
+ case Session::Starting:
+ case Session::AwaitingRearm:
case Session::AwaitingTrigger:
case Session::Running:
session->stop_capture();
diff --git a/pv/mainwindow.hpp b/pv/mainwindow.hpp
index e10d1817..c208f119 100644
--- a/pv/mainwindow.hpp
+++ b/pv/mainwindow.hpp
@@ -163,6 +163,7 @@ private Q_SLOTS:
QTabWidget session_selector_;
QIcon icon_red_;
+ QIcon icon_yellow_;
QIcon icon_green_;
QIcon icon_grey_;
diff --git a/pv/popups/triggermode.cpp b/pv/popups/triggermode.cpp
new file mode 100644
index 00000000..91ce9a6b
--- /dev/null
+++ b/pv/popups/triggermode.cpp
@@ -0,0 +1,86 @@
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2012-2013 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, see .
+ */
+
+#include "triggermode.hpp"
+
+#include
+
+namespace pv {
+namespace popups {
+
+TriggerMode::TriggerMode(Session &session, QWidget *parent) :
+ Popup(parent),
+ session_(session),
+ layout_(this),
+ form_(this),
+ form_layout_(&form_)
+{
+ setWindowTitle(tr("Trigger Mode"));
+
+ form_.setLayout(&form_layout_);
+
+// mode
+
+ QCheckBox *chkbox_repetitive_ = new QCheckBox(tr("&Repetitive"), this);
+
+ QVBoxLayout *vbox_mode_ = new QVBoxLayout;
+ vbox_mode_->addWidget(chkbox_repetitive_);
+
+ QGroupBox *groupbox_mode_ = new QGroupBox(tr("Select the trigger mode"));
+ groupbox_mode_->setLayout(vbox_mode_);
+ form_layout_.addRow(groupbox_mode_);
+
+// delay
+
+ rearm_delay_ = new QSpinBox;
+ rearm_delay_->setRange(100, 2000000000);
+ rearm_delay_->setValue(5000);
+
+ QVBoxLayout *vbox_rearm_delay_ = new QVBoxLayout;
+ vbox_rearm_delay_->addWidget(rearm_delay_);
+
+ QGroupBox *groupbox_rearm_delay_ = new QGroupBox(tr("Set the trigger rearm delay in mSec"));
+ groupbox_rearm_delay_->setLayout(vbox_rearm_delay_);
+ form_layout_.addRow(groupbox_rearm_delay_);
+
+// connections
+
+ connect(chkbox_repetitive_, SIGNAL(toggled(bool)), this, SLOT(chkbox_toggled(bool)));
+ connect(rearm_delay_, SIGNAL(valueChanged(int)), this, SLOT(rearm_time_changed(int)));
+
+ layout_.addWidget(&form_);
+
+// sync with session
+
+ chkbox_repetitive_->setChecked(session_.get_capture_mode() == pv::Repetitive);
+ rearm_delay_->setValue(session_.get_repetitive_rearm_time());
+}
+
+void TriggerMode::chkbox_toggled(bool checked)
+{
+ session_.set_capture_mode(checked ? pv::Repetitive : pv::Single);
+}
+
+void TriggerMode::rearm_time_changed(int value)
+{
+ session_.set_repetitive_rearm_time(value);
+}
+
+} // namespace dialogs
+} // namespace pv
diff --git a/pv/popups/triggermode.hpp b/pv/popups/triggermode.hpp
new file mode 100644
index 00000000..eae71782
--- /dev/null
+++ b/pv/popups/triggermode.hpp
@@ -0,0 +1,79 @@
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2012-2013 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, see .
+ */
+
+#ifndef PULSEVIEW_PV_POPUPS_TRIGGERMODE_HPP
+#define PULSEVIEW_PV_POPUPS_TRIGGERMODE_HPP
+
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+using std::shared_ptr;
+
+namespace pv {
+class Session;
+namespace popups {
+
+class TriggerMode : public pv::widgets::Popup
+{
+ Q_OBJECT
+
+public:
+ TriggerMode(Session &session, QWidget *parent);
+
+private:
+ Session &session_;
+
+private Q_SLOTS:
+ void chkbox_toggled(bool checked);
+ void rearm_time_changed(int value);
+
+private:
+ QVBoxLayout layout_;
+ QWidget form_;
+ QFormLayout form_layout_;
+
+// mode
+
+ QCheckBox *chkbox_repetitive_;
+
+ QVBoxLayout *vbox_mode_;
+ QGroupBox *groupbox_mode_;
+
+// delay
+
+ QSpinBox *rearm_delay_;
+
+ QVBoxLayout *vbox_rearm_delay_;
+ QGroupBox *groupbox_rearm_delay_;
+};
+
+} // namespace dialogs
+} // namespace pv
+
+#endif // PULSEVIEW_PV_POPUPS_TRIGGERMODE_HPP
diff --git a/pv/session.cpp b/pv/session.cpp
index f992d9f1..f5256253 100644
--- a/pv/session.cpp
+++ b/pv/session.cpp
@@ -118,6 +118,10 @@ namespace pv {
shared_ptr Session::sr_context;
Session::Session(DeviceManager &device_manager, QString name) :
+ repetitive_rearm_permitted_(false),
+ capture_mode_(DefaultCaptureMode),
+ repetitive_rearm_time_(DefaultRearmTime),
+ repetitive_rearm_timer_(),
shutting_down_(false),
device_manager_(device_manager),
default_name_(name),
@@ -128,10 +132,24 @@ Session::Session(DeviceManager &device_manager, QString name) :
{
// Use this name also for the QObject instance
setObjectName(name_);
+
+ qRegisterMetaType("util::Timestamp");
+ qRegisterMetaType("uint64_t");
+
+ repetitive_rearm_timer_.stop();
+ repetitive_rearm_timer_.setSingleShot(true);
+ repetitive_rearm_timer_.setInterval(repetitive_rearm_time_);
+
+ connect(&repetitive_rearm_timer_, SIGNAL(timeout()),
+ this, SLOT(on_repetitive_rearm_timeout()));
+ connect(this, SIGNAL(repetitive_rearm()),
+ this, SLOT(on_repetitive_rearm()));
}
Session::~Session()
{
+ repetitive_rearm_timer_.stop();
+
shutting_down_ = true;
// Stop and join to the thread
@@ -829,11 +847,42 @@ void Session::start_capture(function error_handler)
name_changed();
}
+ // Init Segments
+ {
+ lock_guard lock(data_mutex_);
+ cur_logic_segment_.reset();
+ cur_analog_segments_.clear();
+ for (shared_ptr sb : signalbases_)
+ sb->clear_sample_data();
+ }
+ highest_segment_id_ = -1;
+ frame_began_ = false;
+
+ repetitive_rearm_permitted_ = true;
+
+ set_capture_state(capture_state::Starting);
+ start_sampling_thread(error_handler);
+}
+
+void Session::start_sampling_thread(function error_handler)
+{
+ stop_sampling_thread();
+
// Begin the session
sampling_thread_ = std::thread(&Session::sample_thread_proc, this, error_handler);
}
void Session::stop_capture()
+{
+ repetitive_rearm_timer_.stop();
+ repetitive_rearm_permitted_ = false;
+
+ stop_sampling_thread();
+
+ set_capture_state(Stopped);
+}
+
+void Session::stop_sampling_thread()
{
if (get_capture_state() != Stopped)
device_->stop();
@@ -841,6 +890,7 @@ void Session::stop_capture()
// Check that sampling stopped
if (sampling_thread_.joinable())
sampling_thread_.join();
+
}
void Session::register_view(shared_ptr view)
@@ -1052,9 +1102,13 @@ void Session::set_capture_state(capture_state state)
if (state == Running)
acq_time_.restart();
- if (state == Stopped)
+ if (state == Stopped || state == AwaitingRearm)
qDebug("Acquisition took %.2f s", acq_time_.elapsed() / 1000.);
+ if (state == AwaitingRearm) {
+ repetitive_rearm();
+ }
+
{
lock_guard lock(sampling_mutex_);
capture_state_ = state;
@@ -1258,16 +1312,6 @@ void Session::sample_thread_proc(function error_handler)
out_of_memory_ = false;
- {
- lock_guard lock(data_mutex_);
- cur_logic_segment_.reset();
- cur_analog_segments_.clear();
- for (shared_ptr sb : signalbases_)
- sb->clear_sample_data();
- }
- highest_segment_id_ = -1;
- frame_began_ = false;
-
try {
device_->start();
} catch (Error& e) {
@@ -1290,7 +1334,12 @@ void Session::sample_thread_proc(function error_handler)
return;
}
- set_capture_state(Stopped);
+ signal_segment_completed();
+ if (repetitive_rearm_permitted_ && capture_mode_ == Repetitive) {
+ set_capture_state(AwaitingRearm);
+ } else {
+ set_capture_state(Stopped);
+ }
// Confirm that SR_DF_END was received
if (cur_logic_segment_)
@@ -1723,4 +1772,43 @@ void Session::on_new_decoders_selected(vector decoders)
}
#endif
+void Session::on_repetitive_rearm_timeout()
+{
+ repetitive_rearm_timer_.stop();
+ if (capture_mode_ == Repetitive)
+ start_sampling_thread([&](QString message) {
+ // TODO Emulate noquote()
+ qDebug() << "Capture failed:" << message; });
+}
+
+void Session::on_repetitive_rearm()
+{
+ repetitive_rearm_timer_.setInterval(repetitive_rearm_time_);
+ repetitive_rearm_timer_.start();
+}
+
+capture_mode Session::get_capture_mode()
+{
+ return capture_mode_;
+}
+
+void Session::set_capture_mode(capture_mode mode)
+{
+ capture_mode_ = mode;
+ if (capture_mode_ == Single) {
+ repetitive_rearm_permitted_ = false;
+ repetitive_rearm_timer_.stop();
+ }
+}
+
+int Session::get_repetitive_rearm_time()
+{
+ return repetitive_rearm_time_;
+}
+
+void Session::set_repetitive_rearm_time(int repetitive_rearm_time)
+{
+ repetitive_rearm_time_ = repetitive_rearm_time;
+}
+
} // namespace pv
diff --git a/pv/session.hpp b/pv/session.hpp
index 91f98b58..72f8985c 100644
--- a/pv/session.hpp
+++ b/pv/session.hpp
@@ -85,6 +85,14 @@ using sigrok::Option;
namespace pv {
+// TODO rename to?
+// - repeat_mode
+// - rearm_mode
+enum capture_mode {
+ Single,
+ Repetitive
+};
+
class DeviceManager;
namespace data {
@@ -119,8 +127,10 @@ class Session : public QObject
public:
enum capture_state {
Stopped,
+ Starting,
AwaitingTrigger,
- Running
+ Running,
+ AwaitingRearm,
};
static shared_ptr sr_context;
@@ -212,6 +222,11 @@ class Session : public QObject
MetadataObjManager* metadata_obj_manager();
+ capture_mode get_capture_mode();
+ void set_capture_mode(capture_mode);
+ int get_repetitive_rearm_time();
+ void set_repetitive_rearm_time(int);
+
private:
void set_capture_state(capture_state state);
@@ -248,6 +263,16 @@ class Session : public QObject
void data_feed_in(shared_ptr device,
shared_ptr packet);
+ capture_mode const DefaultCaptureMode = Single;
+ int const DefaultRearmTime = 5000; // mSec
+
+ bool repetitive_rearm_permitted_;
+ capture_mode capture_mode_;
+ int repetitive_rearm_time_; // in mSec
+ QTimer repetitive_rearm_timer_;
+ void start_sampling_thread(function error_handler);
+ void stop_sampling_thread();
+
Q_SIGNALS:
void capture_state_changed(int state);
void device_changed();
@@ -264,10 +289,14 @@ class Session : public QObject
void data_received();
void add_view(ViewType type, Session *session);
+ void repetitive_rearm();
public Q_SLOTS:
void on_data_saved();
+ void on_repetitive_rearm_timeout();
+ void on_repetitive_rearm();
+
#ifdef ENABLE_DECODE
void on_new_decoders_selected(vector decoders);
#endif
diff --git a/pv/toolbars/mainbar.cpp b/pv/toolbars/mainbar.cpp
index 39c290df..d6396981 100644
--- a/pv/toolbars/mainbar.cpp
+++ b/pv/toolbars/mainbar.cpp
@@ -46,6 +46,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -106,6 +107,8 @@ MainBar::MainBar(Session &session, QWidget *parent, pv::views::trace::View *view
configure_button_action_(nullptr),
channels_button_(this),
channels_button_action_(nullptr),
+ triggermode_button_(this),
+ triggermode_button_action_(nullptr),
sample_count_(" samples", this),
sample_rate_("Hz", this),
updating_sample_rate_(false),
@@ -269,6 +272,11 @@ MainBar::MainBar(Session &session, QWidget *parent, pv::views::trace::View *view
channels_button_.setToolTip(tr("Configure Channels"));
channels_button_.setIcon(QIcon(":/icons/channels.svg"));
+ triggermode_button_.setToolTip(tr("Configure Repeating Trigger Rearm"));
+ triggermode_button_.setIcon(QIcon(":/icons/trigger-repeat-rearm.svg"));
+ pv::popups::TriggerMode *const triggermode_popup = new pv::popups::TriggerMode(session_, this);
+ triggermode_button_.set_popup(triggermode_popup);
+
add_toolbar_widgets();
sample_count_.installEventFilter(this);
@@ -305,6 +313,7 @@ void MainBar::set_capture_state(pv::Session::capture_state state)
device_selector_.setEnabled(ui_enabled);
configure_button_.setEnabled(ui_enabled);
channels_button_.setEnabled(ui_enabled);
+ triggermode_button_.setEnabled(ui_enabled);
sample_count_.setEnabled(ui_enabled);
sample_rate_.setEnabled(ui_enabled);
}
@@ -925,6 +934,7 @@ void MainBar::add_toolbar_widgets()
addWidget(&device_selector_);
configure_button_action_ = addWidget(&configure_button_);
channels_button_action_ = addWidget(&channels_button_);
+ triggermode_button_action_ = addWidget(&triggermode_button_);
addWidget(&sample_count_);
addWidget(&sample_rate_);
#ifdef ENABLE_DECODE
diff --git a/pv/toolbars/mainbar.hpp b/pv/toolbars/mainbar.hpp
index e4aa39b6..1f4ef03b 100644
--- a/pv/toolbars/mainbar.hpp
+++ b/pv/toolbars/mainbar.hpp
@@ -176,6 +176,9 @@ private Q_SLOTS:
pv::widgets::PopupToolButton channels_button_;
QAction *channels_button_action_;
+ pv::widgets::PopupToolButton triggermode_button_;
+ QAction *triggermode_button_action_;
+
pv::widgets::SweepTimingWidget sample_count_;
pv::widgets::SweepTimingWidget sample_rate_;
bool updating_sample_rate_;
diff --git a/pv/views/trace/standardbar.cpp b/pv/views/trace/standardbar.cpp
index 48d92891..bab0e59f 100644
--- a/pv/views/trace/standardbar.cpp
+++ b/pv/views/trace/standardbar.cpp
@@ -93,7 +93,7 @@ StandardBar::StandardBar(Session &session, QWidget *parent,
action_sdm_single_->setIcon(QIcon(":/icons/view-displaymode-single_segment.svg"));
action_sdm_single_->setText(tr("Display a single segment"));
- connect(action_view_show_cursors_, SIGNAL(triggered(bool)),
+ connect(action_sdm_single_, SIGNAL(triggered(bool)),
this, SLOT(on_actionSDMSingle_triggered()));
segment_display_mode_selector_->addAction(action_sdm_last_);
diff --git a/pv/views/trace/view.cpp b/pv/views/trace/view.cpp
index f1de3b20..ef7ac512 100644
--- a/pv/views/trace/view.cpp
+++ b/pv/views/trace/view.cpp
@@ -782,9 +782,23 @@ void View::set_segment_display_mode(Trace::SegmentDisplayMode mode)
for (const shared_ptr& signal : signals_)
signal->set_segment_display_mode(mode);
+ update_current_segment();
+
+ segment_selectable_ = true;
+
+ if ((mode == Trace::ShowAllSegments) || (mode == Trace::ShowAccumulatedIntensity))
+ segment_selectable_ = false;
+
+ viewport_->update();
+
+ segment_display_mode_changed((int)mode, segment_selectable_);
+}
+
+void View::update_current_segment()
+{
uint32_t last_segment = session_.get_highest_segment_id();
- switch (mode) {
+ switch (segment_display_mode_) {
case Trace::ShowLastSegmentOnly:
if (current_segment_ != last_segment)
set_current_segment(last_segment);
@@ -810,15 +824,6 @@ void View::set_segment_display_mode(Trace::SegmentDisplayMode mode)
// Current segment remains as-is
break;
}
-
- segment_selectable_ = true;
-
- if ((mode == Trace::ShowAllSegments) || (mode == Trace::ShowAccumulatedIntensity))
- segment_selectable_ = false;
-
- viewport_->update();
-
- segment_display_mode_changed((int)mode, segment_selectable_);
}
void View::zoom(double steps)
@@ -1979,7 +1984,7 @@ void View::capture_state_updated(int state)
{
GlobalSettings settings;
- if (state == Session::Running) {
+ if (state == Session::Starting) {
set_time_unit(util::TimeUnit::Samples);
trigger_markers_.clear();
@@ -2002,7 +2007,6 @@ void View::capture_state_updated(int state)
sticky_scrolling_ = !restoring_state_ &&
settings.value(GlobalSettings::Key_View_StickyScrolling).toBool();
- // Reset all traces to segment 0
current_segment_ = 0;
set_current_segment(current_segment_);
}
@@ -2038,33 +2042,21 @@ void View::capture_state_updated(int state)
void View::on_new_segment(int new_segment_id)
{
- on_segment_changed(new_segment_id);
+ update_current_segment();
}
void View::on_segment_completed(int segment_id)
{
- on_segment_changed(segment_id);
+ update_current_segment();
}
void View::on_segment_changed(int segment)
{
- switch (segment_display_mode_) {
- case Trace::ShowLastSegmentOnly:
- case Trace::ShowSingleSegmentOnly:
- set_current_segment(segment);
- break;
+ // Triggered when a segment is selected by UI action from the user
+ // In this case, we assume segment_display_mode_ has already been
+ // reset to ShowSingleSegmentOnly and switch to the given segment.
- case Trace::ShowLastCompleteSegmentOnly:
- // Only update if all segments are complete
- if (session_.all_segments_complete(segment))
- set_current_segment(segment);
- break;
-
- case Trace::ShowAllSegments:
- case Trace::ShowAccumulatedIntensity:
- default:
- break;
- }
+ set_current_segment(segment);
}
void View::on_settingViewTriggerIsZeroTime_changed(const QVariant new_value)
diff --git a/pv/views/trace/view.hpp b/pv/views/trace/view.hpp
index f8506cf4..abca632e 100644
--- a/pv/views/trace/view.hpp
+++ b/pv/views/trace/view.hpp
@@ -422,6 +422,7 @@ public Q_SLOTS:
void update_view_range_metaobject() const;
void update_hover_point();
+ void update_current_segment();
public:
void row_item_appearance_changed(bool label, bool content);