+ lock_guard<mutex> lock(output_mutex_);
+
+ try {
+ const DecodeSegment *segment = &(segments_.at(segment_id));
+ const map<const decode::Row, decode::RowData> *rows =
+ &(segment->annotation_rows);
+
+ const auto iter = rows->find(row);
+ if (iter != rows->end())
+ (*iter).second.get_annotation_subset(dest,
+ start_sample, end_sample);
+ } catch (out_of_range&) {
+ // Do nothing
+ }
+}
+
+void DecodeSignal::save_settings(QSettings &settings) const
+{
+ SignalBase::save_settings(settings);
+
+ settings.setValue("decoders", (int)(stack_.size()));
+
+ // Save decoder stack
+ int decoder_idx = 0;
+ for (shared_ptr<decode::Decoder> decoder : stack_) {
+ settings.beginGroup("decoder" + QString::number(decoder_idx++));
+
+ settings.setValue("id", decoder->decoder()->id);
+
+ // Save decoder options
+ const map<string, GVariant*>& options = decoder->options();
+
+ settings.setValue("options", (int)options.size());
+
+ // Note: decode::Decoder::options() returns only the options
+ // that differ from the default. See binding::Decoder::getter()
+ int i = 0;
+ for (auto option : options) {
+ settings.beginGroup("option" + QString::number(i));
+ settings.setValue("name", QString::fromStdString(option.first));
+ GlobalSettings::store_gvariant(settings, option.second);
+ settings.endGroup();
+ i++;
+ }
+
+ settings.endGroup();
+ }
+
+ // Save channel mapping
+ settings.setValue("channels", (int)channels_.size());
+
+ for (unsigned int channel_id = 0; channel_id < channels_.size(); channel_id++) {
+ auto channel = find_if(channels_.begin(), channels_.end(),
+ [&](data::DecodeChannel ch) { return ch.id == channel_id; });
+
+ if (channel == channels_.end()) {
+ qDebug() << "ERROR: Gap in channel index:" << channel_id;
+ continue;
+ }
+
+ settings.beginGroup("channel" + QString::number(channel_id));
+
+ settings.setValue("name", channel->name); // Useful for debugging
+ settings.setValue("initial_pin_state", channel->initial_pin_state);
+
+ if (channel->assigned_signal)
+ settings.setValue("assigned_signal_name", channel->assigned_signal->name());
+
+ settings.endGroup();
+ }
+}
+
+void DecodeSignal::restore_settings(QSettings &settings)
+{
+ SignalBase::restore_settings(settings);
+
+ // Restore decoder stack
+ GSList *dec_list = g_slist_copy((GSList*)srd_decoder_list());
+
+ int decoders = settings.value("decoders").toInt();
+
+ for (int decoder_idx = 0; decoder_idx < decoders; decoder_idx++) {
+ settings.beginGroup("decoder" + QString::number(decoder_idx));
+
+ QString id = settings.value("id").toString();
+
+ for (GSList *entry = dec_list; entry; entry = entry->next) {
+ const srd_decoder *dec = (srd_decoder*)entry->data;
+ if (!dec)
+ continue;
+
+ if (QString::fromUtf8(dec->id) == id) {
+ shared_ptr<decode::Decoder> decoder =
+ make_shared<decode::Decoder>(dec);
+
+ stack_.push_back(decoder);
+
+ // Restore decoder options that differ from their default
+ int options = settings.value("options").toInt();
+
+ for (int i = 0; i < options; i++) {
+ settings.beginGroup("option" + QString::number(i));
+ QString name = settings.value("name").toString();
+ GVariant *value = GlobalSettings::restore_gvariant(settings);
+ decoder->set_option(name.toUtf8(), value);
+ settings.endGroup();
+ }
+
+ // Include the newly created decode channels in the channel lists
+ update_channel_list();
+ break;
+ }
+ }
+
+ settings.endGroup();
+ channels_updated();
+ }
+
+ // Restore channel mapping
+ unsigned int channels = settings.value("channels").toInt();
+
+ const unordered_set< shared_ptr<data::SignalBase> > signalbases =
+ session_.signalbases();
+
+ for (unsigned int channel_id = 0; channel_id < channels; channel_id++) {
+ auto channel = find_if(channels_.begin(), channels_.end(),
+ [&](data::DecodeChannel ch) { return ch.id == channel_id; });
+
+ if (channel == channels_.end()) {
+ qDebug() << "ERROR: Non-existant channel index:" << channel_id;
+ continue;
+ }
+
+ settings.beginGroup("channel" + QString::number(channel_id));
+
+ QString assigned_signal_name = settings.value("assigned_signal_name").toString();
+
+ for (shared_ptr<data::SignalBase> signal : signalbases)
+ if (signal->name() == assigned_signal_name)
+ channel->assigned_signal = signal.get();
+
+ channel->initial_pin_state = settings.value("initial_pin_state").toInt();
+
+ settings.endGroup();
+ }
+
+ // Update the internal structures
+ stack_config_changed_ = true;
+ update_channel_list();
+ commit_decoder_channels();
+
+ begin_decode();
+}
+
+void DecodeSignal::set_error_message(QString msg)
+{
+ error_message_ = msg;
+ // TODO Emulate noquote()
+ qDebug().nospace() << name() << ": " << msg;
+}
+
+uint32_t DecodeSignal::get_input_segment_count() const
+{
+ uint64_t count = std::numeric_limits<uint64_t>::max();
+ bool no_signals_assigned = true;
+
+ for (const data::DecodeChannel &ch : channels_)
+ if (ch.assigned_signal) {
+ no_signals_assigned = false;
+
+ const shared_ptr<Logic> logic_data = ch.assigned_signal->logic_data();
+ if (!logic_data || logic_data->logic_segments().empty())
+ return 0;
+
+ // Find the min value of all segment counts
+ if ((uint64_t)(logic_data->logic_segments().size()) < count)
+ count = logic_data->logic_segments().size();
+ }
+
+ return (no_signals_assigned ? 0 : count);
+}
+
+uint32_t DecodeSignal::get_input_samplerate(uint32_t segment_id) const
+{
+ double samplerate = 0;
+
+ for (const data::DecodeChannel &ch : channels_)
+ if (ch.assigned_signal) {
+ const shared_ptr<Logic> logic_data = ch.assigned_signal->logic_data();
+ if (!logic_data || logic_data->logic_segments().empty())
+ continue;
+
+ try {
+ const shared_ptr<LogicSegment> segment = logic_data->logic_segments().at(segment_id);
+ samplerate = segment->samplerate();
+ } catch (out_of_range&) {
+ // Do nothing
+ }
+ break;
+ }
+
+ return samplerate;
+}
+
+void DecodeSignal::update_channel_list()
+{
+ vector<data::DecodeChannel> prev_channels = channels_;
+ channels_.clear();
+
+ uint16_t id = 0;
+
+ // Copy existing entries, create new as needed
+ for (shared_ptr<Decoder> decoder : stack_) {
+ const srd_decoder* srd_d = decoder->decoder();
+ const GSList *l;
+
+ // Mandatory channels
+ for (l = srd_d->channels; l; l = l->next) {
+ const struct srd_channel *const pdch = (struct srd_channel *)l->data;
+ bool ch_added = false;
+
+ // Copy but update ID if this channel was in the list before
+ for (data::DecodeChannel &ch : prev_channels)
+ if (ch.pdch_ == pdch) {
+ ch.id = id++;
+ channels_.push_back(ch);
+ ch_added = true;
+ break;
+ }
+
+ if (!ch_added) {
+ // Create new entry without a mapped signal
+ data::DecodeChannel ch = {id++, 0, false, nullptr,
+ QString::fromUtf8(pdch->name), QString::fromUtf8(pdch->desc),
+ SRD_INITIAL_PIN_SAME_AS_SAMPLE0, decoder, pdch};
+ channels_.push_back(ch);
+ }
+ }
+
+ // Optional channels
+ for (l = srd_d->opt_channels; l; l = l->next) {
+ const struct srd_channel *const pdch = (struct srd_channel *)l->data;
+ bool ch_added = false;
+
+ // Copy but update ID if this channel was in the list before
+ for (data::DecodeChannel &ch : prev_channels)
+ if (ch.pdch_ == pdch) {
+ ch.id = id++;
+ channels_.push_back(ch);
+ ch_added = true;
+ break;
+ }
+
+ if (!ch_added) {
+ // Create new entry without a mapped signal
+ data::DecodeChannel ch = {id++, 0, true, nullptr,
+ QString::fromUtf8(pdch->name), QString::fromUtf8(pdch->desc),
+ SRD_INITIAL_PIN_SAME_AS_SAMPLE0, decoder, pdch};
+ channels_.push_back(ch);
+ }
+ }
+ }
+
+ // 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::commit_decoder_channels()
+{
+ // Submit channel list to every decoder, containing only the relevant channels
+ for (shared_ptr<decode::Decoder> dec : stack_) {
+ vector<data::DecodeChannel*> channel_list;
+
+ for (data::DecodeChannel &ch : channels_)
+ if (ch.decoder_ == dec)
+ channel_list.push_back(&ch);
+
+ dec->set_channels(channel_list);
+ }
+
+ // Channel bit IDs must be in sync with the channel's apperance in channels_
+ int id = 0;
+ for (data::DecodeChannel &ch : channels_)
+ if (ch.assigned_signal)
+ ch.bit_id = id++;
+}
+
+void DecodeSignal::mux_logic_samples(uint32_t segment_id, const int64_t start, const int64_t end)
+{
+ // Enforce end to be greater than start
+ if (end <= start)
+ return;
+
+ // Fetch the channel segments and their data
+ vector<shared_ptr<LogicSegment> > segments;
+ vector<const uint8_t*> signal_data;
+ vector<uint8_t> signal_in_bytepos;
+ vector<uint8_t> signal_in_bitpos;
+
+ for (data::DecodeChannel &ch : channels_)
+ if (ch.assigned_signal) {
+ const shared_ptr<Logic> logic_data = ch.assigned_signal->logic_data();
+
+ shared_ptr<LogicSegment> segment;
+ try {
+ segment = logic_data->logic_segments().at(segment_id);
+ } catch (out_of_range&) {
+ qDebug() << "Muxer error for" << name() << ":" << ch.assigned_signal->name() \
+ << "has no logic segment" << segment_id;
+ return;
+ }
+ segments.push_back(segment);
+
+ uint8_t* data = new uint8_t[(end - start) * segment->unit_size()];
+ segment->get_samples(start, end, data);
+ signal_data.push_back(data);
+
+ const int bitpos = ch.assigned_signal->logic_bit_index();
+ signal_in_bytepos.push_back(bitpos / 8);
+ signal_in_bitpos.push_back(bitpos % 8);
+ }
+
+
+ shared_ptr<LogicSegment> output_segment;
+ try {
+ output_segment = logic_mux_data_->logic_segments().at(segment_id);
+ } catch (out_of_range&) {
+ qDebug() << "Muxer error for" << name() << ": no logic mux segment" \
+ << segment_id << "in mux_logic_samples(), mux segments size is" \
+ << logic_mux_data_->logic_segments().size();
+ return;
+ }
+
+ // Perform the muxing of signal data into the output data
+ uint8_t* output = new uint8_t[(end - start) * output_segment->unit_size()];
+ unsigned int signal_count = signal_data.size();
+
+ for (int64_t sample_cnt = 0; !logic_mux_interrupt_ && (sample_cnt < (end - start));
+ sample_cnt++) {
+
+ int bitpos = 0;
+ uint8_t bytepos = 0;
+
+ const int out_sample_pos = sample_cnt * output_segment->unit_size();
+ for (unsigned int i = 0; i < output_segment->unit_size(); i++)
+ output[out_sample_pos + i] = 0;
+
+ for (unsigned int i = 0; i < signal_count; i++) {
+ const int in_sample_pos = sample_cnt * segments[i]->unit_size();
+ const uint8_t in_sample = 1 &
+ ((signal_data[i][in_sample_pos + signal_in_bytepos[i]]) >> (signal_in_bitpos[i]));
+
+ const uint8_t out_sample = output[out_sample_pos + bytepos];
+
+ output[out_sample_pos + bytepos] = out_sample | (in_sample << bitpos);
+
+ bitpos++;
+ if (bitpos > 7) {
+ bitpos = 0;
+ bytepos++;
+ }
+ }
+ }
+
+ output_segment->append_payload(output, (end - start) * output_segment->unit_size());
+ delete[] output;
+
+ for (const uint8_t* data : signal_data)
+ delete[] data;
+}
+
+void DecodeSignal::logic_mux_proc()
+{
+ uint32_t segment_id = 0;
+
+ assert(logic_mux_data_);
+
+ // Create initial logic mux segment
+ shared_ptr<LogicSegment> output_segment =
+ make_shared<LogicSegment>(*logic_mux_data_, segment_id,
+ logic_mux_unit_size_, 0);
+ logic_mux_data_->push_segment(output_segment);
+
+ output_segment->set_samplerate(get_input_samplerate(0));
+
+ do {
+ const uint64_t input_sample_count = get_working_sample_count(segment_id);
+ const uint64_t output_sample_count = output_segment->get_sample_count();
+
+ const uint64_t samples_to_process =
+ (input_sample_count > output_sample_count) ?
+ (input_sample_count - output_sample_count) : 0;
+
+ // Process the samples if necessary...
+ if (samples_to_process > 0) {
+ const uint64_t unit_size = output_segment->unit_size();
+ const uint64_t chunk_sample_count = DecodeChunkLength / unit_size;
+
+ uint64_t processed_samples = 0;
+ do {
+ const uint64_t start_sample = output_sample_count + processed_samples;
+ const uint64_t sample_count =
+ min(samples_to_process - processed_samples, chunk_sample_count);
+
+ mux_logic_samples(segment_id, start_sample, start_sample + sample_count);
+ processed_samples += sample_count;
+
+ // ...and process the newly muxed logic data
+ decode_input_cond_.notify_one();
+ } while (!logic_mux_interrupt_ && (processed_samples < samples_to_process));
+ }
+
+ if (samples_to_process == 0) {
+ // TODO Optimize this by caching the input segment count and only
+ // querying it when the cached value was reached
+ if (segment_id < get_input_segment_count() - 1) {
+ // Process next segment
+ segment_id++;
+
+ output_segment =
+ make_shared<LogicSegment>(*logic_mux_data_, segment_id,
+ logic_mux_unit_size_, 0);
+ logic_mux_data_->push_segment(output_segment);
+
+ output_segment->set_samplerate(get_input_samplerate(segment_id));
+
+ } else {
+ // All segments have been processed
+ logic_mux_data_invalid_ = false;
+
+ // Wait for more input
+ unique_lock<mutex> logic_mux_lock(logic_mux_mutex_);
+ logic_mux_cond_.wait(logic_mux_lock);
+ }
+ }
+ } while (!logic_mux_interrupt_);