#include "analog.hpp"
#include "analogsegment.hpp"
+#include "decode/row.hpp"
#include "logic.hpp"
#include "logicsegment.hpp"
#include "signalbase.hpp"
#include "signaldata.hpp"
-#include "decode/row.hpp"
-#include <pv/session.hpp>
#include <pv/binding/decoder.hpp>
+#include <pv/session.hpp>
using std::dynamic_pointer_cast;
using std::make_shared;
using std::shared_ptr;
using std::tie;
+using std::unique_lock;
namespace pv {
namespace data {
const int SignalBase::ColourBGAlpha = 8 * 256 / 100;
+const uint64_t SignalBase::ConversionBlockSize = 4096;
SignalBase::SignalBase(shared_ptr<sigrok::Channel> channel, ChannelType channel_type) :
channel_(channel),
SignalBase::~SignalBase()
{
- // Wait for the currently ongoing conversion to finish
- if (conversion_thread_.joinable())
- conversion_thread_.join();
+ stop_conversion();
}
shared_ptr<sigrok::Channel> SignalBase::channel() const
unsigned int SignalBase::index() const
{
- return (channel_) ? channel_->index() : (unsigned int)-1;
+ return (channel_) ? channel_->index() : 0;
+}
+
+unsigned int SignalBase::logic_bit_index() const
+{
+ if (channel_type_ == LogicChannel)
+ return channel_->index();
+ else
+ return 0;
}
QColor SignalBase::colour() const
void SignalBase::set_data(shared_ptr<pv::data::SignalData> data)
{
- if (data_ && channel_type_ == AnalogChannel) {
- shared_ptr<Analog> analog_data = dynamic_pointer_cast<Analog>(data_);
-
- disconnect(analog_data.get(), SIGNAL(samples_cleared()),
+ if (data_) {
+ disconnect(data.get(), SIGNAL(samples_cleared()),
this, SLOT(on_samples_cleared()));
- disconnect(analog_data.get(), SIGNAL(samples_added(QObject*, uint64_t, uint64_t)),
+ disconnect(data.get(), SIGNAL(samples_added(QObject*, uint64_t, uint64_t)),
this, SLOT(on_samples_added(QObject*, uint64_t, uint64_t)));
}
data_ = data;
- if (data_ && channel_type_ == AnalogChannel) {
- shared_ptr<Analog> analog_data = dynamic_pointer_cast<Analog>(data_);
-
- connect(analog_data.get(), SIGNAL(samples_cleared()),
+ if (data_) {
+ connect(data.get(), SIGNAL(samples_cleared()),
this, SLOT(on_samples_cleared()));
- connect(analog_data.get(), SIGNAL(samples_added(QObject*, uint64_t, uint64_t)),
+ connect(data.get(), SIGNAL(samples_added(QObject*, uint64_t, uint64_t)),
this, SLOT(on_samples_added(QObject*, uint64_t, uint64_t)));
}
}
void SignalBase::set_conversion_type(ConversionType t)
{
if (conversion_type_ != NoConversion) {
- // Wait for the currently ongoing conversion to finish
- if (conversion_thread_.joinable())
- conversion_thread_.join();
+ stop_conversion();
// Discard converted data
converted_data_.reset();
+ samples_cleared();
}
conversion_type_ = t;
- if ((channel_type_ == AnalogChannel) &&
- ((conversion_type_ == A2LConversionByTreshold) ||
- (conversion_type_ == A2LConversionBySchmittTrigger))) {
-
- shared_ptr<Analog> analog_data = dynamic_pointer_cast<Analog>(data_);
+ // Re-create an empty container
+ // so that the signal is recognized as providing logic data
+ // and thus can be assigned to a decoder
+ if (conversion_is_a2l())
+ if (!converted_data_)
+ converted_data_ = make_shared<Logic>(1); // Contains only one channel
- if (analog_data->analog_segments().size() > 0) {
- AnalogSegment *asegment = analog_data->analog_segments().front().get();
-
- // Begin conversion of existing sample data
- // TODO Support for multiple segments is missing
- on_samples_added(asegment, 0, 0);
- }
- }
+ start_conversion();
conversion_type_changed(t);
}
#ifdef ENABLE_DECODE
bool SignalBase::is_decode_signal() const
{
- return (decoder_stack_ != nullptr);
-}
-
-shared_ptr<pv::data::DecoderStack> SignalBase::decoder_stack() const
-{
- return decoder_stack_;
-}
-
-void SignalBase::set_decoder_stack(shared_ptr<pv::data::DecoderStack>
- decoder_stack)
-{
- decoder_stack_ = decoder_stack;
+ return (channel_type_ == DecodeChannel);
}
#endif
set_conversion_type((ConversionType)settings.value("conversion_type").toInt());
}
-uint8_t SignalBase::convert_a2l_threshold(float threshold, float value)
+bool SignalBase::conversion_is_a2l() const
{
- return (value >= threshold) ? 1 : 0;
+ return ((channel_type_ == AnalogChannel) &&
+ ((conversion_type_ == A2LConversionByTreshold) ||
+ (conversion_type_ == A2LConversionBySchmittTrigger)));
}
-uint8_t SignalBase::convert_a2l_schmitt_trigger(float lo_thr, float hi_thr,
- float value, uint8_t &state)
+void SignalBase::conversion_thread_proc(QObject* segment)
{
- if (value < lo_thr)
- state = 0;
- else if (value > hi_thr)
- state = 1;
+ // TODO Support for multiple segments is missing
- return state;
-}
+ uint64_t start_sample, end_sample;
+ start_sample = end_sample = 0;
-void SignalBase::conversion_thread_proc(QObject* segment, uint64_t start_sample,
- uint64_t end_sample)
-{
- const uint64_t block_size = 4096;
+ do {
+ if (conversion_is_a2l()) {
- // TODO Support for multiple segments is missing
+ AnalogSegment *asegment = qobject_cast<AnalogSegment*>(segment);
- if ((channel_type_ == AnalogChannel) &&
- ((conversion_type_ == A2LConversionByTreshold) ||
- (conversion_type_ == A2LConversionBySchmittTrigger))) {
-
- AnalogSegment *asegment = qobject_cast<AnalogSegment*>(segment);
-
- // Create the logic data container if needed
- shared_ptr<Logic> logic_data;
- if (!converted_data_) {
- logic_data = make_shared<Logic>(1); // Contains only one channel
- converted_data_ = logic_data;
- } else
- logic_data = dynamic_pointer_cast<Logic>(converted_data_);
-
- // Create the initial logic data segment if needed
- if (logic_data->segments().size() == 0) {
- shared_ptr<LogicSegment> lsegment =
- make_shared<LogicSegment>(*logic_data.get(), 1, asegment->samplerate());
- logic_data->push_segment(lsegment);
- }
+ const shared_ptr<Logic> logic_data = dynamic_pointer_cast<Logic>(converted_data_);
+
+ // Create the initial logic data segment if needed
+ if (logic_data->segments().size() == 0) {
+ shared_ptr<LogicSegment> lsegment =
+ make_shared<LogicSegment>(*logic_data.get(), 1, asegment->samplerate());
+ logic_data->push_segment(lsegment);
+ }
- LogicSegment *lsegment = dynamic_cast<LogicSegment*>(logic_data->segments().front().get());
+ LogicSegment *lsegment = dynamic_cast<LogicSegment*>(logic_data->segments().front().get());
- // start_sample=end_sample=0 means we need to figure out the unprocessed range
- if ((start_sample == 0) && (end_sample == 0)) {
start_sample = lsegment->get_sample_count();
end_sample = asegment->get_sample_count();
- }
- if (start_sample == end_sample)
- return; // Nothing to do
+ if (end_sample > start_sample) {
+ float min_v, max_v;
+ tie(min_v, max_v) = asegment->get_min_max();
+
+ // Create sigrok::Analog instance
+ float *asamples = new float[ConversionBlockSize];
+ uint8_t *lsamples = new uint8_t[ConversionBlockSize];
+
+ vector<shared_ptr<sigrok::Channel> > channels;
+ channels.push_back(channel_);
+
+ vector<const sigrok::QuantityFlag*> mq_flags;
+ const sigrok::Quantity * const mq = sigrok::Quantity::VOLTAGE;
+ const sigrok::Unit * const unit = sigrok::Unit::VOLT;
+
+ shared_ptr<sigrok::Packet> packet =
+ Session::sr_context->create_analog_packet(channels,
+ asamples, ConversionBlockSize, mq, unit, mq_flags);
- float min_v, max_v;
- tie(min_v, max_v) = asegment->get_min_max();
+ shared_ptr<sigrok::Analog> analog =
+ dynamic_pointer_cast<sigrok::Analog>(packet->payload());
- vector<uint8_t> lsamples;
- lsamples.reserve(block_size);
+ // Convert
+ uint64_t i = start_sample;
- uint64_t i = start_sample;
+ if (conversion_type_ == A2LConversionByTreshold) {
+ const float threshold = (min_v + max_v) * 0.5; // middle between min and max
- if (conversion_type_ == A2LConversionByTreshold) {
- const float threshold = (min_v + max_v) * 0.5; // middle between min and max
+ // Convert as many sample blocks as we can
+ while ((end_sample - i) > ConversionBlockSize) {
+ asegment->get_samples(i, i + ConversionBlockSize, asamples);
- // Convert as many sample blocks as we can
- while ((end_sample - i) > block_size) {
- const float* asamples = asegment->get_samples(i, i + block_size);
- for (uint32_t j = 0; j < block_size; j++)
- lsamples.push_back(convert_a2l_threshold(threshold, asamples[j]));
- lsegment->append_payload(lsamples.data(), lsamples.size());
- i += block_size;
- lsamples.clear();
+ shared_ptr<sigrok::Logic> logic =
+ analog->get_logic_via_threshold(threshold, lsamples);
+
+ lsegment->append_payload(logic->data_pointer(), logic->data_length());
+
+ samples_added(lsegment, i, i + ConversionBlockSize);
+ i += ConversionBlockSize;
+ }
+
+ // Re-create sigrok::Analog and convert remaining samples
+ packet = Session::sr_context->create_analog_packet(channels,
+ asamples, end_sample - i, mq, unit, mq_flags);
+
+ analog = dynamic_pointer_cast<sigrok::Analog>(packet->payload());
+
+ asegment->get_samples(i, end_sample, asamples);
+ shared_ptr<sigrok::Logic> logic =
+ analog->get_logic_via_threshold(threshold, lsamples);
+ lsegment->append_payload(logic->data_pointer(), logic->data_length());
+ samples_added(lsegment, i, end_sample);
+ }
+
+ if (conversion_type_ == A2LConversionBySchmittTrigger) {
+ const float amplitude = max_v - min_v;
+ const float center = min_v + (amplitude / 2);
+ const float lo_thr = center - (amplitude * 0.15); // 15% margin
+ const float hi_thr = center + (amplitude * 0.15); // 15% margin
+ uint8_t state = 0; // TODO Use value of logic sample n-1 instead of 0
+
+ // Convert as many sample blocks as we can
+ while ((end_sample - i) > ConversionBlockSize) {
+ asegment->get_samples(i, i + ConversionBlockSize, asamples);
+
+ shared_ptr<sigrok::Logic> logic =
+ analog->get_logic_via_schmitt_trigger(lo_thr, hi_thr,
+ &state, lsamples);
+
+ lsegment->append_payload(logic->data_pointer(), logic->data_length());
+
+ samples_added(lsegment, i, i + ConversionBlockSize);
+ i += ConversionBlockSize;
+ }
+
+ // Re-create sigrok::Analog and convert remaining samples
+ packet = Session::sr_context->create_analog_packet(channels,
+ asamples, end_sample - i, mq, unit, mq_flags);
+
+ analog = dynamic_pointer_cast<sigrok::Analog>(packet->payload());
+
+ asegment->get_samples(i, end_sample, asamples);
+ shared_ptr<sigrok::Logic> logic =
+ analog->get_logic_via_schmitt_trigger(lo_thr, hi_thr,
+ &state, lsamples);
+ lsegment->append_payload(logic->data_pointer(), logic->data_length());
+ samples_added(lsegment, i, end_sample);
+ }
+
+ // If acquisition is ongoing, start-/endsample may have changed
+ end_sample = asegment->get_sample_count();
+
+ delete[] lsamples;
delete[] asamples;
}
+ }
- // Convert remaining samples
- const float* asamples = asegment->get_samples(i, end_sample);
- for (uint32_t j = 0; j < (end_sample - i); j++)
- lsamples.push_back(convert_a2l_threshold(threshold, asamples[j]));
- lsegment->append_payload(lsamples.data(), lsamples.size());
- delete[] asamples;
+ if (!conversion_interrupt_ && (start_sample == end_sample)) {
+ unique_lock<mutex> input_lock(conversion_input_mutex_);
+ conversion_input_cond_.wait(input_lock);
}
+ } while (!conversion_interrupt_);
+}
- if (conversion_type_ == A2LConversionBySchmittTrigger) {
- const float amplitude = max_v - min_v;
- const float lo_thr = min_v + (amplitude * 0.1); // 10% above min
- const float hi_thr = max_v - (amplitude * 0.1); // 10% below max
- uint8_t state = 0; // TODO Use value of logic sample n-1 instead of 0
-
- // Convert as many sample blocks as we can
- while ((end_sample - i) > block_size) {
- const float* asamples = asegment->get_samples(i, i + block_size);
- for (uint32_t j = 0; j < block_size; j++)
- lsamples.push_back(convert_a2l_schmitt_trigger(lo_thr, hi_thr, asamples[j], state));
- lsegment->append_payload(lsamples.data(), lsamples.size());
- i += block_size;
- lsamples.clear();
- delete[] asamples;
- }
+void SignalBase::start_conversion()
+{
+ stop_conversion();
+
+ if (conversion_is_a2l()) {
+ shared_ptr<Analog> analog_data = dynamic_pointer_cast<Analog>(data_);
- // Convert remaining samples
- const float* asamples = asegment->get_samples(i, end_sample);
- for (uint32_t j = 0; j < (end_sample - i); j++)
- lsamples.push_back(convert_a2l_schmitt_trigger(lo_thr, hi_thr, asamples[j], state));
- lsegment->append_payload(lsamples.data(), lsamples.size());
- delete[] asamples;
+ if (analog_data->analog_segments().size() > 0) {
+ // TODO Support for multiple segments is missing
+ AnalogSegment *asegment = analog_data->analog_segments().front().get();
+
+ conversion_interrupt_ = false;
+ conversion_thread_ = std::thread(
+ &SignalBase::conversion_thread_proc, this, asegment);
}
}
}
+void SignalBase::stop_conversion()
+{
+ // Stop conversion so we can restart it from the beginning
+ conversion_interrupt_ = true;
+ conversion_input_cond_.notify_one();
+ if (conversion_thread_.joinable())
+ conversion_thread_.join();
+}
+
void SignalBase::on_samples_cleared()
{
if (converted_data_)
converted_data_->clear();
+
+ samples_cleared();
}
void SignalBase::on_samples_added(QObject* segment, uint64_t start_sample,
uint64_t end_sample)
{
- (void)segment;
- (void)start_sample;
- (void)end_sample;
-
if (conversion_type_ != NoConversion) {
-
- // Wait for the currently ongoing conversion to finish
- if (conversion_thread_.joinable())
- conversion_thread_.join();
-
- conversion_thread_ = std::thread(
- &SignalBase::conversion_thread_proc, this,
- segment, start_sample, end_sample);
+ if (conversion_thread_.joinable()) {
+ // Notify the conversion thread since it's running
+ conversion_input_cond_.notify_one();
+ } else {
+ // Start the conversion thread
+ start_conversion();
+ }
}
+
+ samples_added(segment, start_sample, end_sample);
}
void SignalBase::on_capture_state_changed(int state)
{
- return;
- if (state == Session::Stopped) {
- // Make sure that all data is converted
-
- if ((channel_type_ == AnalogChannel) &&
- ((conversion_type_ == A2LConversionByTreshold) ||
- (conversion_type_ == A2LConversionBySchmittTrigger))) {
-
- shared_ptr<Analog> analog_data = dynamic_pointer_cast<Analog>(data_);
-
- if (analog_data->analog_segments().size() > 0) {
- // TODO Support for multiple segments is missing
- AnalogSegment *asegment = analog_data->analog_segments().front().get();
-
- if (conversion_thread_.joinable())
- conversion_thread_.join();
-
- conversion_thread_ = std::thread(
- &SignalBase::conversion_thread_proc, this, asegment, 0, 0);
- }
+ if (state == Session::Running) {
+ if (conversion_type_ != NoConversion) {
+ // Restart conversion
+ stop_conversion();
+ start_conversion();
}
}
}