From: Joel Holdsworth Date: Tue, 29 Oct 2013 18:12:55 +0000 (+0000) Subject: Implemented decoder stacking X-Git-Tag: pulseview-0.2.0~219 X-Git-Url: https://sigrok.org/gitaction?a=commitdiff_plain;h=7491a29f33471b38e161b3254f4d8e708b05f929;p=pulseview.git Implemented decoder stacking --- diff --git a/CMakeLists.txt b/CMakeLists.txt index 0f7502a4..f2159f5e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -112,6 +112,7 @@ set(pulseview_SOURCES pv/data/logicsnapshot.cpp pv/data/signaldata.cpp pv/data/snapshot.cpp + pv/data/decode/decoder.cpp pv/dialogs/about.cpp pv/dialogs/connect.cpp pv/popups/deviceoptions.cpp diff --git a/pv/data/decode/decoder.cpp b/pv/data/decode/decoder.cpp new file mode 100644 index 00000000..77f91cc3 --- /dev/null +++ b/pv/data/decode/decoder.cpp @@ -0,0 +1,105 @@ +/* + * This file is part of the PulseView project. + * + * Copyright (C) 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, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include "decoder.h" + +#include + +using namespace boost; +using namespace std; + +namespace pv { +namespace data { +namespace decode { + +Decoder::Decoder(const srd_decoder *const dec) : + _decoder(dec), + _options(g_hash_table_new_full(g_str_hash, + g_str_equal, g_free, (GDestroyNotify)g_variant_unref)) +{ +} + +Decoder::~Decoder() +{ + g_hash_table_destroy(_options); +} + +const srd_decoder* Decoder::decoder() const +{ + return _decoder; +} + +const map >& +Decoder::probes() const +{ + return _probes; +} + +void Decoder::set_probes(std::map > probes) +{ + _probes = probes; +} + +const GHashTable* Decoder::options() const +{ + return _options; +} + +void Decoder::set_option(const char *id, GVariant *value) +{ + g_variant_ref(value); + g_hash_table_replace(_options, (void*)g_strdup(id), value); +} + +srd_decoder_inst* Decoder::create_decoder_inst( + srd_session *const session) const +{ + // Create the decoder instance + srd_decoder_inst *const decoder_inst = srd_inst_new( + session, _decoder->id, _options); + if(!decoder_inst) + return NULL; + + // Setup the probes + GHashTable *const probes = g_hash_table_new_full(g_str_hash, + g_str_equal, g_free, (GDestroyNotify)g_variant_unref); + + for(map >:: + const_iterator i = _probes.begin(); + i != _probes.end(); i++) + { + shared_ptr signal((*i).second); + GVariant *const gvar = g_variant_new_int32( + signal->probe()->index); + g_variant_ref_sink(gvar); + g_hash_table_insert(probes, (*i).first->id, gvar); + } + + srd_inst_probe_set_all(decoder_inst, probes); + + return decoder_inst; +} + +} // decode +} // data +} // pv diff --git a/pv/data/decode/decoder.h b/pv/data/decode/decoder.h new file mode 100644 index 00000000..e9c5726f --- /dev/null +++ b/pv/data/decode/decoder.h @@ -0,0 +1,76 @@ +/* + * This file is part of the PulseView project. + * + * Copyright (C) 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, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef PULSEVIEW_PV_DATA_DECODE_DECODER_H +#define PULSEVIEW_PV_DATA_DECODE_DECODER_H + +#include + +#include + +#include + +struct srd_decoder; +struct srd_decoder_inst; +struct srd_probe; +struct srd_session; + +namespace pv { + +namespace view { +class LogicSignal; +} + +namespace data { +namespace decode { + +class Decoder +{ +public: + Decoder(const srd_decoder *const decoder); + + virtual ~Decoder(); + + const srd_decoder* decoder() const; + + const std::map >& probes() const; + void set_probes(std::map > probes); + + const GHashTable* options() const; + + void set_option(const char *id, GVariant *value); + + srd_decoder_inst* create_decoder_inst( + srd_session *const session) const; + +private: + const srd_decoder *const _decoder; + std::map > + _probes; + GHashTable *_options; +}; + +} // namespace decode +} // namespace data +} // namespace pv + +#endif // PULSEVIEW_PV_DATA_DECODE_DECODER_H diff --git a/pv/data/decoderstack.cpp b/pv/data/decoderstack.cpp index d56422a9..2817d292 100644 --- a/pv/data/decoderstack.cpp +++ b/pv/data/decoderstack.cpp @@ -20,6 +20,7 @@ #include +#include #include #include @@ -30,6 +31,7 @@ #include #include +#include #include #include @@ -45,49 +47,28 @@ const int64_t DecoderStack::DecodeChunkLength = 4096; mutex DecoderStack::_global_decode_mutex; -DecoderStack::DecoderStack(const srd_decoder *const dec) : - _decoder(dec), - _options(g_hash_table_new_full(g_str_hash, - g_str_equal, g_free, (GDestroyNotify)g_variant_unref)) +DecoderStack::DecoderStack(const srd_decoder *const dec) { + _stack.push_back(shared_ptr( + new decode::Decoder(dec))); } DecoderStack::~DecoderStack() { _decode_thread.interrupt(); _decode_thread.join(); - - g_hash_table_destroy(_options); -} - -const srd_decoder* DecoderStack::decoder() const -{ - return _decoder; -} - -const map >& -DecoderStack::probes() const -{ - return _probes; -} - -void DecoderStack::set_probes(std::map > probes) -{ - _probes = probes; - begin_decode(); } -const GHashTable* DecoderStack::options() const +const std::list< boost::shared_ptr >& +DecoderStack::stack() const { - return _options; + return _stack; } -void DecoderStack::set_option(const char *id, GVariant *value) +void DecoderStack::push(boost::shared_ptr decoder) { - g_variant_ref(value); - g_hash_table_replace(_options, (void*)g_strdup(id), value); - begin_decode(); + assert(decoder); + _stack.push_back(decoder); } const vector< shared_ptr > @@ -105,35 +86,31 @@ QString DecoderStack::error_message() void DecoderStack::begin_decode() { + shared_ptr logic_signal; + shared_ptr data; + _decode_thread.interrupt(); _decode_thread.join(); _annotations.clear(); - if (_probes.empty()) - return; - - // Get the samplerate and start time - shared_ptr logic_signal = - dynamic_pointer_cast( - (*_probes.begin()).second); - if (logic_signal) { - shared_ptr data( - logic_signal->data()); - if (data) { - _start_time = data->get_start_time(); - _samplerate = data->get_samplerate(); - if (_samplerate == 0.0) - _samplerate = 1.0; - } - } - // We get the logic data of the first probe in the list. // This works because we are currently assuming all // LogicSignals have the same data/snapshot - shared_ptr sig = (*_probes.begin()).second; - assert(sig); - shared_ptr data = sig->data(); + BOOST_FOREACH (const shared_ptr &dec, _stack) + if (dec && !dec->probes().empty() && + ((logic_signal = (*dec->probes().begin()).second)) && + ((data = logic_signal->data()))) + break; + + if (!data) + return; + + // Get the samplerate and start time + _start_time = data->get_start_time(); + _samplerate = data->get_samplerate(); + if (_samplerate == 0.0) + _samplerate = 1.0; _decode_thread = boost::thread(&DecoderStack::decode_proc, this, data); @@ -147,6 +124,7 @@ void DecoderStack::decode_proc(shared_ptr data) { srd_session *session; uint8_t chunk[DecodeChunkLength]; + srd_decoder_inst *prev_di = NULL; assert(data); @@ -163,38 +141,32 @@ void DecoderStack::decode_proc(shared_ptr data) srd_session_new(&session); assert(session); - srd_session_metadata_set(session, SRD_CONF_SAMPLERATE, - g_variant_new_uint64((uint64_t)_samplerate)); - srd_pd_output_callback_add(session, SRD_OUTPUT_ANN, - DecoderStack::annotation_callback, this); + // Create the decoders + BOOST_FOREACH(const shared_ptr &dec, _stack) + { + srd_decoder_inst *const di = dec->create_decoder_inst(session); - // Create the decoder instance - srd_decoder_inst *const decoder_inst = srd_inst_new( - session, _decoder->id, _options); - if(!decoder_inst) { - _error_message = tr("Failed to initialise decoder"); - return; - } + if (!di) + { + _error_message = tr("Failed to initialise decoder"); + srd_session_destroy(session); + return; + } - // Setup the probes - GHashTable *const probes = g_hash_table_new_full(g_str_hash, - g_str_equal, g_free, (GDestroyNotify)g_variant_unref); + if (prev_di) + srd_inst_stack (session, prev_di, di); - for(map >:: - const_iterator i = _probes.begin(); - i != _probes.end(); i++) - { - shared_ptr signal((*i).second); - GVariant *const gvar = g_variant_new_int32( - signal->probe()->index); - g_variant_ref_sink(gvar); - g_hash_table_insert(probes, (*i).first->id, gvar); + prev_di = di; } - srd_inst_probe_set_all(decoder_inst, probes); - // Start the session + srd_session_metadata_set(session, SRD_CONF_SAMPLERATE, + g_variant_new_uint64((uint64_t)_samplerate)); + + srd_pd_output_callback_add(session, SRD_OUTPUT_ANN, + DecoderStack::annotation_callback, this); + srd_session_start(session); for (int64_t i = 0; diff --git a/pv/data/decoderstack.h b/pv/data/decoderstack.h index 844a2b54..8b9d29b2 100644 --- a/pv/data/decoderstack.h +++ b/pv/data/decoderstack.h @@ -23,7 +23,7 @@ #include "signaldata.h" -#include +#include #include #include @@ -31,8 +31,6 @@ #include #include -#include - struct srd_decoder; struct srd_probe; struct srd_proto_data; @@ -54,6 +52,10 @@ class Annotation; namespace data { +namespace decode { +class Decoder; +} + class Logic; class DecoderStack : public QObject, public SignalData @@ -70,16 +72,8 @@ public: virtual ~DecoderStack(); - const srd_decoder* decoder() const; - - const std::map >& - probes() const; - void set_probes(std::map > probes); - - const GHashTable* options() const; - - void set_option(const char *id, GVariant *value); + const std::list< boost::shared_ptr >& stack() const; + void push(boost::shared_ptr decoder); const std::vector< boost::shared_ptr > annotations() const; @@ -88,9 +82,9 @@ public: void clear_snapshots(); -private: void begin_decode(); +private: void decode_proc(boost::shared_ptr data); static void annotation_callback(srd_proto_data *pdata, @@ -109,10 +103,7 @@ private: */ static boost::mutex _global_decode_mutex; - const srd_decoder *const _decoder; - std::map > - _probes; - GHashTable *_options; + std::list< boost::shared_ptr > _stack; mutable boost::mutex _mutex; std::vector< boost::shared_ptr > diff --git a/pv/prop/binding/decoderoptions.cpp b/pv/prop/binding/decoderoptions.cpp index 0a6cd15c..34f1b813 100644 --- a/pv/prop/binding/decoderoptions.cpp +++ b/pv/prop/binding/decoderoptions.cpp @@ -22,10 +22,12 @@ #include "decoderoptions.h" +#include #include #include #include +#include #include #include @@ -36,7 +38,10 @@ namespace pv { namespace prop { namespace binding { -DecoderOptions::DecoderOptions(shared_ptr decoder) : +DecoderOptions::DecoderOptions( + shared_ptr decoder_stack, + shared_ptr decoder) : + _decoder_stack(decoder_stack), _decoder(decoder) { assert(_decoder); @@ -105,6 +110,9 @@ void DecoderOptions::setter(const char *id, GVariant *value) { assert(_decoder); _decoder->set_option(id, value); + + assert(_decoder_stack); + _decoder_stack->begin_decode(); } } // binding diff --git a/pv/prop/binding/decoderoptions.h b/pv/prop/binding/decoderoptions.h index e507e87c..62f4fe18 100644 --- a/pv/prop/binding/decoderoptions.h +++ b/pv/prop/binding/decoderoptions.h @@ -29,6 +29,9 @@ namespace pv { namespace data { class DecoderStack; +namespace decode { +class Decoder; +} } namespace prop { @@ -37,7 +40,8 @@ namespace binding { class DecoderOptions : public Binding { public: - DecoderOptions(boost::shared_ptr decoder); + DecoderOptions(boost::shared_ptr decoder_stack, + boost::shared_ptr decoder); private: GVariant* getter(const char *id); @@ -45,7 +49,8 @@ private: void setter(const char *id, GVariant *value); private: - boost::shared_ptr _decoder; + boost::shared_ptr _decoder_stack; + boost::shared_ptr _decoder; }; } // binding diff --git a/pv/sigsession.cpp b/pv/sigsession.cpp index a6a2875e..8936574a 100644 --- a/pv/sigsession.cpp +++ b/pv/sigsession.cpp @@ -29,6 +29,7 @@ #include "data/decoderstack.h" #include "data/logic.h" #include "data/logicsnapshot.h" +#include "data/decode/decoder.h" #include "view/analogsignal.h" #include "view/decodetrace.h" @@ -202,13 +203,15 @@ boost::shared_ptr SigSession::get_data() bool SigSession::add_decoder(srd_decoder *const dec) { map > probes; + shared_ptr decoder_stack; try { lock_guard lock(_signals_mutex); // Create the decoder - shared_ptr decoder(new data::DecoderStack(dec)); + decoder_stack = shared_ptr( + new data::DecoderStack(dec)); // Auto select the initial probes for(const GSList *i = dec->probes; i; i = i->next) @@ -224,11 +227,14 @@ bool SigSession::add_decoder(srd_decoder *const dec) } } - decoder->set_probes(probes); + assert(decoder_stack); + assert(!decoder_stack->stack().empty()); + assert(decoder_stack->stack().front()); + decoder_stack->stack().front()->set_probes(probes); // Create the decode signal shared_ptr d( - new view::DecodeTrace(*this, decoder, + new view::DecodeTrace(*this, decoder_stack, _decode_traces.size())); _decode_traces.push_back(d); } @@ -239,6 +245,9 @@ bool SigSession::add_decoder(srd_decoder *const dec) signals_changed(); + // Do an initial decode + decoder_stack->begin_decode(); + return true; } diff --git a/pv/view/decodetrace.cpp b/pv/view/decodetrace.cpp index 51b17e5e..7e2051a4 100644 --- a/pv/view/decodetrace.cpp +++ b/pv/view/decodetrace.cpp @@ -37,6 +37,7 @@ extern "C" { #include #include +#include #include #include #include @@ -59,9 +60,8 @@ const QColor DecodeTrace::ErrorBgColour = QColor(0xEF, 0x29, 0x29); DecodeTrace::DecodeTrace(pv::SigSession &session, boost::shared_ptr decoder_stack, int index) : - Trace(session, QString(decoder_stack->decoder()->name)), - _decoder_stack(decoder_stack), - _binding(decoder_stack) + Trace(session, QString(decoder_stack->stack().front()->decoder()->name)), + _decoder_stack(decoder_stack) { assert(_decoder_stack); @@ -130,63 +130,32 @@ void DecodeTrace::paint_mid(QPainter &p, int left, int right) void DecodeTrace::populate_popup_form(QWidget *parent, QFormLayout *form) { - const GSList *probe; - assert(form); assert(parent); assert(_decoder_stack); - const srd_decoder *const decoder = _decoder_stack->decoder(); - - assert(decoder); - + // Add the standard options Trace::populate_popup_form(parent, form); - form->addRow(new QLabel(tr("

Probes

"), parent)); - - _probe_selector_map.clear(); - - // Add the mandatory probes - for(probe = decoder->probes; probe; probe = probe->next) { - const struct srd_probe *const p = - (struct srd_probe *)probe->data; - QComboBox *const combo = create_probe_selector(parent, p); - connect(combo, SIGNAL(currentIndexChanged(int)), - this, SLOT(on_probe_selected(int))); - form->addRow(tr("%1 (%2) *") - .arg(p->name).arg(p->desc), combo); - - _probe_selector_map[p] = combo; - } - - // Add the optional probes - for(probe = decoder->opt_probes; probe; probe = probe->next) { - const struct srd_probe *const p = - (struct srd_probe *)probe->data; - QComboBox *const combo = create_probe_selector(parent, p); - connect(combo, SIGNAL(currentIndexChanged(int)), - this, SLOT(on_probe_selected(int))); - form->addRow(tr("%1 (%2)") - .arg(p->name).arg(p->desc), combo); + // Add the decoder options + _bindings.clear(); + _probe_selectors.clear(); - _probe_selector_map[p] = combo; - } + BOOST_FOREACH(shared_ptr dec, + _decoder_stack->stack()) + create_decoder_form(dec, parent, form); form->addRow(new QLabel( tr("* Required Probes"), parent)); - // Add the options - if (!_binding.properties().empty()) { - form->addRow(new QLabel(tr("

Options

"), - parent)); - _binding.add_properties_to_form(form, true); - } - // Add stacking button - QPushButton *const stack_button = - new QPushButton(tr("Stack DecoderStack"), parent); pv::widgets::DecoderMenu *const decoder_menu = new pv::widgets::DecoderMenu(parent); + connect(decoder_menu, SIGNAL(decoder_selected(srd_decoder*)), + this, SLOT(on_stack_decoder(srd_decoder*))); + + QPushButton *const stack_button = + new QPushButton(tr("Stack Decoder"), parent); stack_button->setMenu(decoder_menu); QHBoxLayout *stack_button_box = new QHBoxLayout; @@ -229,21 +198,71 @@ void DecodeTrace::draw_error(QPainter &p, const QString &message, p.drawText(text_rect, message); } +void DecodeTrace::create_decoder_form(shared_ptr &dec, + QWidget *parent, QFormLayout *form) +{ + const GSList *probe; + + assert(dec); + const srd_decoder *const decoder = dec->decoder(); + assert(decoder); + + form->addRow(new QLabel(tr("

%1

").arg(decoder->name), parent)); + + // Add the mandatory probes + for(probe = decoder->probes; probe; probe = probe->next) { + const struct srd_probe *const p = + (struct srd_probe *)probe->data; + QComboBox *const combo = create_probe_selector(parent, dec, p); + connect(combo, SIGNAL(currentIndexChanged(int)), + this, SLOT(on_probe_selected(int))); + form->addRow(tr("%1 (%2) *") + .arg(p->name).arg(p->desc), combo); + + const ProbeSelector s = {combo, dec, p}; + _probe_selectors.push_back(s); + } + + // Add the optional probes + for(probe = decoder->opt_probes; probe; probe = probe->next) { + const struct srd_probe *const p = + (struct srd_probe *)probe->data; + QComboBox *const combo = create_probe_selector(parent, dec, p); + connect(combo, SIGNAL(currentIndexChanged(int)), + this, SLOT(on_probe_selected(int))); + form->addRow(tr("%1 (%2)") + .arg(p->name).arg(p->desc), combo); + + const ProbeSelector s = {combo, dec, p}; + _probe_selectors.push_back(s); + } + + // Add the options + shared_ptr binding( + new prop::binding::DecoderOptions(_decoder_stack, dec)); + binding->add_properties_to_form(form, true); + + _bindings.push_back(binding); +} + QComboBox* DecodeTrace::create_probe_selector( - QWidget *parent, const srd_probe *const probe) + QWidget *parent, const shared_ptr &dec, + const srd_probe *const probe) { + assert(dec); + const vector< shared_ptr > sigs = _session.get_signals(); assert(_decoder_stack); const map >::const_iterator probe_iter = - _decoder_stack->probes().find(probe); + dec->probes().find(probe); QComboBox *selector = new QComboBox(parent); selector->addItem("-", qVariantFromValue((void*)NULL)); - if (probe_iter == _decoder_stack->probes().end()) + if (probe_iter == dec->probes().end()) selector->setCurrentIndex(0); for(size_t i = 0; i < sigs.size(); i++) { @@ -262,31 +281,41 @@ QComboBox* DecodeTrace::create_probe_selector( return selector; } -void DecodeTrace::commit_probes() +void DecodeTrace::commit_decoder_probes(shared_ptr &dec) { - assert(_decoder_stack); + assert(dec); map > probe_map; const vector< shared_ptr > sigs = _session.get_signals(); - for(map::const_iterator i = - _probe_selector_map.begin(); - i != _probe_selector_map.end(); i++) + BOOST_FOREACH(const ProbeSelector &s, _probe_selectors) { - const QComboBox *const combo = (*i).second; + if(s._decoder != dec) + break; + const LogicSignal *const selection = - (LogicSignal*)combo->itemData(combo->currentIndex()). - value(); + (LogicSignal*)s._combo->itemData( + s._combo->currentIndex()).value(); - BOOST_FOREACH(shared_ptr s, sigs) - if(s.get() == selection) { - probe_map[(*i).first] = - dynamic_pointer_cast(s); + BOOST_FOREACH(shared_ptr sig, sigs) + if(sig.get() == selection) { + probe_map[s._probe] = + dynamic_pointer_cast(sig); break; } } - _decoder_stack->set_probes(probe_map); + dec->set_probes(probe_map); +} + +void DecodeTrace::commit_probes() +{ + assert(_decoder_stack); + BOOST_FOREACH(shared_ptr dec, + _decoder_stack->stack()) + commit_decoder_probes(dec); + + _decoder_stack->begin_decode(); } void DecodeTrace::on_new_decode_data() @@ -310,5 +339,14 @@ void DecodeTrace::on_probe_selected(int) commit_probes(); } +void DecodeTrace::on_stack_decoder(srd_decoder *decoder) +{ + assert(decoder); + assert(_decoder_stack); + _decoder_stack->push(shared_ptr( + new data::decode::Decoder(decoder))); + _decoder_stack->begin_decode(); +} + } // namespace view } // namespace pv diff --git a/pv/view/decodetrace.h b/pv/view/decodetrace.h index 229df86d..c12d5b51 100644 --- a/pv/view/decodetrace.h +++ b/pv/view/decodetrace.h @@ -23,6 +23,7 @@ #include "trace.h" +#include #include #include @@ -37,6 +38,10 @@ namespace pv { namespace data { class DecoderStack; + +namespace decode { +class Decoder; +} } namespace view { @@ -45,6 +50,14 @@ class DecodeTrace : public Trace { Q_OBJECT +private: + struct ProbeSelector + { + const QComboBox *_combo; + const boost::shared_ptr _decoder; + const srd_probe *_probe; + }; + private: static const QColor DecodeColours[4]; static const QColor ErrorBgColour; @@ -86,8 +99,16 @@ private: void draw_error(QPainter &p, const QString &message, int left, int right); - QComboBox* create_probe_selector( - QWidget *parent, const srd_probe *const probe); + void create_decoder_form( + boost::shared_ptr &dec, + QWidget *parent, QFormLayout *form); + + QComboBox* create_probe_selector(QWidget *parent, + const boost::shared_ptr &dec, + const srd_probe *const probe); + + void commit_decoder_probes( + boost::shared_ptr &dec); void commit_probes(); @@ -98,14 +119,17 @@ private slots: void on_probe_selected(int); + void on_stack_decoder(srd_decoder *decoder); + private: boost::shared_ptr _decoder_stack; uint64_t _decode_start, _decode_end; - pv::prop::binding::DecoderOptions _binding; + std::list< boost::shared_ptr > + _bindings; - std::map _probe_selector_map; + std::list _probe_selectors; }; } // namespace view diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 5cc18c16..18deb63b 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -46,6 +46,7 @@ set(pulseview_TEST_SOURCES ${PROJECT_SOURCE_DIR}/pv/data/logicsnapshot.cpp ${PROJECT_SOURCE_DIR}/pv/data/snapshot.cpp ${PROJECT_SOURCE_DIR}/pv/data/signaldata.cpp + ${PROJECT_SOURCE_DIR}/pv/data/decode/decoder.cpp ${PROJECT_SOURCE_DIR}/pv/prop/int.cpp ${PROJECT_SOURCE_DIR}/pv/prop/property.cpp ${PROJECT_SOURCE_DIR}/pv/prop/string.cpp