X-Git-Url: https://sigrok.org/gitweb/?p=pulseview.git;a=blobdiff_plain;f=pv%2Fdata%2Fdecodesignal.cpp;h=14bef5d45bcecc6a89cad2345a6b15f145eecbe1;hp=5b2bb31d75e1217e7567d634f289eaab0985bd03;hb=477472187338948c83bea5d790ead66034008296;hpb=132a5c6d4b3c220d1cb6d942bf9d7e8b180ab1c3;ds=sidebyside
diff --git a/pv/data/decodesignal.cpp b/pv/data/decodesignal.cpp
index 5b2bb31d..14bef5d4 100644
--- a/pv/data/decodesignal.cpp
+++ b/pv/data/decodesignal.cpp
@@ -17,6 +17,10 @@
* along with this program; if not, see .
*/
+#include
+
+#include
+
#include "logic.hpp"
#include "logicsegment.hpp"
#include "decodesignal.hpp"
@@ -25,78 +29,110 @@
#include
#include
#include
-#include
#include
+using boost::optional;
+using std::lock_guard;
+using std::make_pair;
using std::make_shared;
+using std::min;
using std::shared_ptr;
+using std::unique_lock;
+using pv::data::decode::Annotation;
using pv::data::decode::Decoder;
using pv::data::decode::Row;
namespace pv {
namespace data {
-DecodeSignal::DecodeSignal(shared_ptr decoder_stack,
- const unordered_set< shared_ptr > &all_signals) :
- SignalBase(nullptr, SignalBase::DecodeChannel),
- decoder_stack_(decoder_stack),
- all_signals_(all_signals)
-{
- set_name(QString::fromUtf8(decoder_stack_->stack().front()->decoder()->name));
+const double DecodeSignal::DecodeMargin = 1.0;
+const double DecodeSignal::DecodeThreshold = 0.2;
+const int64_t DecodeSignal::DecodeChunkLength = 10 * 1024 * 1024;
+const unsigned int DecodeSignal::DecodeNotifyPeriod = 1024;
- update_channel_list();
- auto_assign_signals();
+mutex DecodeSignal::global_srd_mutex_;
- connect(decoder_stack_.get(), SIGNAL(new_annotations()),
- this, SLOT(on_new_annotations()));
-}
-DecodeSignal::~DecodeSignal()
+DecodeSignal::DecodeSignal(pv::Session &session) :
+ SignalBase(nullptr, SignalBase::DecodeChannel),
+ session_(session),
+ logic_mux_data_invalid_(false),
+ start_time_(0),
+ samplerate_(0),
+ sample_count_(0),
+ annotation_count_(0),
+ samples_decoded_(0),
+ frame_complete_(false)
{
+ connect(&session_, SIGNAL(capture_state_changed(int)),
+ this, SLOT(on_capture_state_changed(int)));
+ connect(&session_, SIGNAL(data_received()),
+ this, SLOT(on_data_received()));
+ connect(&session_, SIGNAL(frame_ended()),
+ this, SLOT(on_frame_ended()));
+
+ set_name(tr("Empty decoder signal"));
}
-bool DecodeSignal::is_decode_signal() const
+DecodeSignal::~DecodeSignal()
{
- return true;
-}
+ if (decode_thread_.joinable()) {
+ decode_interrupt_ = true;
+ decode_input_cond_.notify_one();
+ decode_thread_.join();
+ }
-shared_ptr DecodeSignal::decoder_stack() const
-{
- return decoder_stack_;
+ if (logic_mux_thread_.joinable()) {
+ logic_mux_interrupt_ = true;
+ logic_mux_cond_.notify_one();
+ logic_mux_thread_.join();
+ }
}
-const list< shared_ptr >& DecodeSignal::decoder_stack_list() const
+const vector< shared_ptr >& DecodeSignal::decoder_stack() const
{
- return decoder_stack_->stack();
+ return stack_;
}
void DecodeSignal::stack_decoder(srd_decoder *decoder)
{
assert(decoder);
- assert(decoder_stack);
- decoder_stack_->push(make_shared(decoder));
+ stack_.push_back(make_shared(decoder));
+
+ // Set name if this decoder is the first in the list
+ if (stack_.size() == 1)
+ set_name(QString::fromUtf8(decoder->name));
// Include the newly created decode channels in the channel list
update_channel_list();
auto_assign_signals();
- decoder_stack_->begin_decode();
+ begin_decode();
}
void DecodeSignal::remove_decoder(int index)
{
- decoder_stack_->remove(index);
+ assert(index >= 0);
+ assert(index < (int)stack_.size());
+
+ // Find the decoder in the stack
+ auto iter = stack_.begin();
+ for (int i = 0; i < index; i++, iter++)
+ assert(iter != stack_.end());
+
+ // Delete the element
+ stack_.erase(iter);
+
+ // Update channels and decoded data
update_channel_list();
- decoder_stack_->begin_decode();
+ begin_decode();
}
bool DecodeSignal::toggle_decoder_visibility(int index)
{
- const list< shared_ptr > stack(decoder_stack_->stack());
-
- auto iter = stack.cbegin();
+ auto iter = stack_.cbegin();
for (int i = 0; i < index; i++, iter++)
- assert(iter != stack.end());
+ assert(iter != stack_.end());
shared_ptr dec = *iter;
@@ -110,38 +146,127 @@ bool DecodeSignal::toggle_decoder_visibility(int index)
return state;
}
+void DecodeSignal::reset_decode()
+{
+ sample_count_ = 0;
+ annotation_count_ = 0;
+ frame_complete_ = false;
+ samples_decoded_ = 0;
+ error_message_ = QString();
+ rows_.clear();
+ class_rows_.clear();
+}
+
+void DecodeSignal::begin_decode()
+{
+ if (decode_thread_.joinable()) {
+ decode_interrupt_ = true;
+ decode_input_cond_.notify_one();
+ decode_thread_.join();
+ }
+
+ if (logic_mux_thread_.joinable()) {
+ logic_mux_interrupt_ = true;
+ logic_mux_cond_.notify_one();
+ logic_mux_thread_.join();
+ }
+
+ reset_decode();
+
+ // Check that all decoders have the required channels
+ for (const shared_ptr &dec : stack_)
+ if (!dec->have_required_channels()) {
+ error_message_ = tr("One or more required channels "
+ "have not been specified");
+ return;
+ }
+
+ // Add annotation classes
+ for (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;
+ }
+ }
+
+ // Make sure the logic output data is complete and up-to-date
+ logic_mux_thread_ = std::thread(&DecodeSignal::logic_mux_proc, this);
+
+ // Update the samplerate and start time
+ start_time_ = segment_->start_time();
+ samplerate_ = segment_->samplerate();
+ if (samplerate_ == 0.0)
+ samplerate_ = 1.0;
+
+ decode_interrupt_ = false;
+ decode_thread_ = std::thread(&DecodeSignal::decode_proc, this);
+}
+
QString DecodeSignal::error_message() const
{
- return decoder_stack_->error_message();
+ lock_guard lock(output_mutex_);
+ return error_message_;
}
-const list DecodeSignal::get_channels() const
+const vector DecodeSignal::get_channels() const
{
return channels_;
}
void DecodeSignal::auto_assign_signals()
{
+ bool new_assignment = false;
+
// Try to auto-select channels that don't have signals assigned yet
for (data::DecodeChannel &ch : channels_) {
if (ch.assigned_signal)
continue;
- for (shared_ptr s : all_signals_)
- if (s->logic_data() && (ch.name.toLower().contains(s->name().toLower())))
+ for (shared_ptr s : session_.signalbases())
+ if (s->logic_data() && (ch.name.toLower().contains(s->name().toLower()))) {
ch.assigned_signal = s.get();
+ new_assignment = true;
+ }
+ }
+
+ if (new_assignment) {
+ logic_mux_data_invalid_ = true;
+ channels_updated();
}
}
void DecodeSignal::assign_signal(const uint16_t channel_id, const SignalBase *signal)
{
for (data::DecodeChannel &ch : channels_)
- if (ch.id == channel_id)
+ if (ch.id == channel_id) {
ch.assigned_signal = signal;
+ logic_mux_data_invalid_ = true;
+ }
channels_updated();
- decoder_stack_->begin_decode();
+ begin_decode();
}
void DecodeSignal::set_initial_pin_state(const uint16_t channel_id, const int init_state)
@@ -152,12 +277,80 @@ void DecodeSignal::set_initial_pin_state(const uint16_t channel_id, const int in
channels_updated();
- decoder_stack_->begin_decode();
+ begin_decode();
+}
+
+double DecodeSignal::samplerate() const
+{
+ return samplerate_;
+}
+
+const pv::util::Timestamp& DecodeSignal::start_time() const
+{
+ return start_time_;
+}
+
+int64_t DecodeSignal::get_working_sample_count() const
+{
+ // The working sample count is the highest sample number for
+ // which all used signals have data available, so go through
+ // all channels and use the lowest overall sample count of the
+ // current segment
+
+ // TODO Currently, we assume only a single segment exists
+
+ int64_t count = std::numeric_limits::max();
+ bool no_signals_assigned = true;
+
+ for (const data::DecodeChannel &ch : channels_)
+ if (ch.assigned_signal) {
+ no_signals_assigned = false;
+
+ const shared_ptr logic_data = ch.assigned_signal->logic_data();
+ if (!logic_data || logic_data->logic_segments().empty())
+ return 0;
+
+ const shared_ptr segment = logic_data->logic_segments().front();
+ count = min(count, (int64_t)segment->get_sample_count());
+ }
+
+ return (no_signals_assigned ? 0 : count);
+}
+
+int64_t DecodeSignal::get_decoded_sample_count() const
+{
+ lock_guard decode_lock(output_mutex_);
+ return samples_decoded_;
}
vector DecodeSignal::visible_rows() const
{
- return decoder_stack_->get_visible_rows();
+ lock_guard lock(output_mutex_);
+
+ vector rows;
+
+ for (const shared_ptr &dec : stack_) {
+ assert(dec);
+ if (!dec->shown())
+ continue;
+
+ 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.emplace_back(decc);
+
+ // 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.emplace_back(decc, ann_row);
+ }
+ }
+
+ return rows;
}
void DecodeSignal::get_annotation_subset(
@@ -165,19 +358,42 @@ void DecodeSignal::get_annotation_subset(
const decode::Row &row, uint64_t start_sample,
uint64_t end_sample) const
{
- return decoder_stack_->get_annotation_subset(dest, row,
- start_sample, end_sample);
+ lock_guard lock(output_mutex_);
+
+ const auto iter = rows_.find(row);
+ if (iter != rows_.end())
+ (*iter).second.get_annotation_subset(dest,
+ start_sample, end_sample);
+}
+
+void DecodeSignal::save_settings(QSettings &settings) const
+{
+ SignalBase::save_settings(settings);
+
+ // TODO Save decoder stack, channel mapping and decoder options
+}
+
+void DecodeSignal::restore_settings(QSettings &settings)
+{
+ SignalBase::restore_settings(settings);
+
+ // TODO Restore decoder stack, channel mapping and decoder options
+}
+
+uint64_t DecodeSignal::inc_annotation_count()
+{
+ return (annotation_count_++);
}
void DecodeSignal::update_channel_list()
{
- list prev_channels = channels_;
+ vector prev_channels = channels_;
channels_.clear();
uint16_t id = 0;
// Copy existing entries, create new as needed
- for (shared_ptr decoder : decoder_stack_->stack()) {
+ for (shared_ptr decoder : stack_) {
const srd_decoder* srd_d = decoder->decoder();
const GSList *l;
@@ -187,7 +403,7 @@ void DecodeSignal::update_channel_list()
bool ch_added = false;
// Copy but update ID if this channel was in the list before
- for (data::DecodeChannel ch : prev_channels)
+ for (data::DecodeChannel &ch : prev_channels)
if (ch.pdch_ == pdch) {
ch.id = id++;
channels_.push_back(ch);
@@ -210,7 +426,7 @@ void DecodeSignal::update_channel_list()
bool ch_added = false;
// Copy but update ID if this channel was in the list before
- for (data::DecodeChannel ch : prev_channels)
+ for (data::DecodeChannel &ch : prev_channels)
if (ch.pdch_ == pdch) {
ch.id = id++;
channels_.push_back(ch);
@@ -228,13 +444,203 @@ void DecodeSignal::update_channel_list()
}
}
+ // Invalidate the logic output data if the channel assignment changed
+ if (prev_channels.size() != channels_.size()) {
+ // The number of channels changed, there's definitely a difference
+ logic_mux_data_invalid_ = true;
+ } else {
+ // Same number but assignment may still differ, so compare all channels
+ for (size_t i = 0; i < channels_.size(); i++) {
+ const data::DecodeChannel &p_ch = prev_channels[i];
+ const data::DecodeChannel &ch = channels_[i];
+
+ if ((p_ch.pdch_ != ch.pdch_) ||
+ (p_ch.assigned_signal != ch.assigned_signal)) {
+ logic_mux_data_invalid_ = true;
+ break;
+ }
+ }
+
+ }
+
channels_updated();
}
-void DecodeSignal::on_new_annotations()
+void DecodeSignal::logic_mux_proc()
+{
+
+}
+
+optional DecodeSignal::wait_for_data() const
{
- // Forward the signal to the frontend
+ unique_lock input_lock(input_mutex_);
+
+ // Do wait if we decoded all samples but we're still capturing
+ // Do not wait if we're done capturing
+ while (!decode_interrupt_ && !frame_complete_ &&
+ (samples_decoded_ >= sample_count_) &&
+ (session_.get_capture_state() != Session::Stopped)) {
+
+ decode_input_cond_.wait(input_lock);
+ }
+
+ // Return value is valid if we're not aborting the decode,
+ return boost::make_optional(!decode_interrupt_ &&
+ // and there's more work to do...
+ (samples_decoded_ < sample_count_ || !frame_complete_) &&
+ // and if the end of the data hasn't been reached yet
+ (!((samples_decoded_ >= sample_count_) && (session_.get_capture_state() == Session::Stopped))),
+ sample_count_);
+}
+
+void DecodeSignal::decode_data(
+ const int64_t abs_start_samplenum, const int64_t sample_count,
+ srd_session *const session)
+{
+ const unsigned int unit_size = segment_->unit_size();
+ const unsigned int chunk_sample_count = DecodeChunkLength / unit_size;
+
+ for (int64_t i = abs_start_samplenum;
+ !decode_interrupt_ && (i < (abs_start_samplenum + sample_count));
+ i += chunk_sample_count) {
+
+ const int64_t chunk_end = min(i + chunk_sample_count,
+ abs_start_samplenum + sample_count);
+
+ const uint8_t* chunk = segment_->get_samples(i, chunk_end);
+
+ 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");
+ delete[] chunk;
+ break;
+ }
+ delete[] chunk;
+
+ {
+ lock_guard lock(output_mutex_);
+ samples_decoded_ = chunk_end;
+ }
+ }
+}
+
+void DecodeSignal::decode_proc()
+{
+ optional sample_count;
+ srd_session *session;
+ srd_decoder_inst *prev_di = nullptr;
+
+ // Prevent any other decode threads from accessing libsigrokdecode
+ lock_guard srd_lock(global_srd_mutex_);
+
+ // Create the session
+ srd_session_new(&session);
+ assert(session);
+
+ // Create the decoders
+ for (const shared_ptr &dec : stack_) {
+ srd_decoder_inst *const di = dec->create_decoder_inst(session);
+
+ if (!di) {
+ error_message_ = tr("Failed to create decoder instance");
+ srd_session_destroy(session);
+ return;
+ }
+
+ if (prev_di)
+ srd_inst_stack(session, prev_di, di);
+
+ prev_di = di;
+ }
+
+ // Get the initial sample count
+ {
+ unique_lock input_lock(input_mutex_);
+ sample_count = sample_count_ = get_working_sample_count();
+ }
+
+ // Start the session
+ srd_session_metadata_set(session, SRD_CONF_SAMPLERATE,
+ g_variant_new_uint64(samplerate_));
+
+ srd_pd_output_callback_add(session, SRD_OUTPUT_ANN,
+ DecodeSignal::annotation_callback, this);
+
+ srd_session_start(session);
+
+ int64_t abs_start_samplenum = 0;
+ do {
+ decode_data(abs_start_samplenum, *sample_count, session);
+ abs_start_samplenum = *sample_count;
+ } while (error_message_.isEmpty() && (sample_count = wait_for_data()));
+
+ // Make sure all annotations are known to the frontend
new_annotations();
+
+ // Destroy the session
+ srd_session_destroy(session);
+}
+
+void DecodeSignal::annotation_callback(srd_proto_data *pdata, void *decode_signal)
+{
+ assert(pdata);
+ assert(decoder);
+
+ DecodeSignal *const ds = (DecodeSignal*)decode_signal;
+ assert(ds);
+
+ lock_guard lock(ds->output_mutex_);
+
+ const decode::Annotation a(pdata);
+
+ // Find the row
+ assert(pdata->pdo);
+ assert(pdata->pdo->di);
+ const srd_decoder *const decc = pdata->pdo->di->decoder;
+ assert(decc);
+
+ auto row_iter = ds->rows_.end();
+
+ // Try looking up the sub-row of this class
+ const auto r = ds->class_rows_.find(make_pair(decc, a.format()));
+ if (r != ds->class_rows_.end())
+ row_iter = ds->rows_.find((*r).second);
+ else {
+ // Failing that, use the decoder as a key
+ row_iter = ds->rows_.find(Row(decc));
+ }
+
+ assert(row_iter != ds->rows_.end());
+ if (row_iter == ds->rows_.end()) {
+ qDebug() << "Unexpected annotation: decoder = " << decc <<
+ ", format = " << a.format();
+ assert(false);
+ return;
+ }
+
+ // Add the annotation
+ (*row_iter).second.push_annotation(a);
+
+ // Notify the frontend every DecodeNotifyPeriod annotations
+ if (ds->inc_annotation_count() % DecodeNotifyPeriod == 0)
+ ds->new_annotations();
+}
+
+void DecodeSignal::on_capture_state_changed(int state)
+{
+ // If a new acquisition was started, we need to start decoding from scratch
+ if (state == Session::Running)
+ begin_decode();
+}
+
+void DecodeSignal::on_data_received()
+{
+ logic_mux_cond_.notify_one();
+}
+
+void DecodeSignal::on_frame_ended()
+{
+ logic_mux_cond_.notify_one();
}
} // namespace data