X-Git-Url: https://sigrok.org/gitweb/?p=pulseview.git;a=blobdiff_plain;f=pv%2Fdata%2Fsignalbase.cpp;h=e267885d8d214356d6e19692decf19cf7f56d796;hp=b43e5bb07f4db8e92c9c797aeb6daa1efc16ce55;hb=c6d9cf6582589fd375e67579d42ee914ce6382a1;hpb=5e95d3d980490ce37d3ae404998b8c89089391e3 diff --git a/pv/data/signalbase.cpp b/pv/data/signalbase.cpp index b43e5bb0..e267885d 100644 --- a/pv/data/signalbase.cpp +++ b/pv/data/signalbase.cpp @@ -40,14 +40,22 @@ namespace data { const int SignalBase::ColourBGAlpha = 8 * 256 / 100; const uint64_t SignalBase::ConversionBlockSize = 4096; +const uint32_t SignalBase::ConversionDelay = 1000; // 1 second SignalBase::SignalBase(shared_ptr channel, ChannelType channel_type) : channel_(channel), channel_type_(channel_type), - conversion_type_(NoConversion) + conversion_type_(NoConversion), + min_value_(0), + max_value_(0) { if (channel_) internal_name_ = QString::fromStdString(channel_->name()); + + connect(&delayed_conversion_starter_, SIGNAL(timeout()), + this, SLOT(on_delayed_conversion_start())); + delayed_conversion_starter_.setSingleShot(true); + delayed_conversion_starter_.setInterval(ConversionDelay); } SignalBase::~SignalBase() @@ -138,6 +146,14 @@ void SignalBase::set_data(shared_ptr data) this, SLOT(on_samples_cleared())); disconnect(data.get(), SIGNAL(samples_added(QObject*, uint64_t, uint64_t)), this, SLOT(on_samples_added(QObject*, uint64_t, uint64_t))); + + if (channel_type_ == AnalogChannel) { + shared_ptr analog = analog_data(); + assert(analog); + + disconnect(analog.get(), SIGNAL(min_max_changed(float, float)), + this, SLOT(on_min_max_changed(float, float))); + } } data_ = data; @@ -147,6 +163,14 @@ void SignalBase::set_data(shared_ptr data) this, SLOT(on_samples_cleared())); connect(data.get(), SIGNAL(samples_added(QObject*, uint64_t, uint64_t)), this, SLOT(on_samples_added(QObject*, uint64_t, uint64_t))); + + if (channel_type_ == AnalogChannel) { + shared_ptr analog = analog_data(); + assert(analog); + + connect(analog.get(), SIGNAL(min_max_changed(float, float)), + this, SLOT(on_min_max_changed(float, float))); + } } } @@ -167,13 +191,18 @@ shared_ptr SignalBase::logic_data() const if (channel_type_ == LogicChannel) result = dynamic_pointer_cast(data_); - if (((conversion_type_ == A2LConversionByTreshold) || + if (((conversion_type_ == A2LConversionByThreshold) || (conversion_type_ == A2LConversionBySchmittTrigger))) result = dynamic_pointer_cast(converted_data_); return result; } +SignalBase::ConversionType SignalBase::get_conversion_type() const +{ + return conversion_type_; +} + void SignalBase::set_conversion_type(ConversionType t) { if (conversion_type_ != NoConversion) { @@ -198,6 +227,131 @@ void SignalBase::set_conversion_type(ConversionType t) conversion_type_changed(t); } +map SignalBase::get_conversion_options() const +{ + return conversion_options_; +} + +bool SignalBase::set_conversion_option(QString key, QVariant value) +{ + QVariant old_value; + + auto key_iter = conversion_options_.find(key); + if (key_iter != conversion_options_.end()) + old_value = key_iter->second; + + conversion_options_[key] = value; + + return (value != old_value); +} + +vector SignalBase::get_conversion_thresholds(const ConversionType t, + const bool always_custom) const +{ + vector result; + ConversionType conv_type = t; + ConversionPreset preset; + + // Use currently active conversion if no conversion type was supplied + if (conv_type == NoConversion) + conv_type = conversion_type_; + + if (always_custom) + preset = NoPreset; + else + preset = get_current_conversion_preset(); + + if (conv_type == A2LConversionByThreshold) { + double thr = 0; + + if (preset == NoPreset) { + auto thr_iter = conversion_options_.find("threshold_value"); + if (thr_iter != conversion_options_.end()) + thr = (thr_iter->second).toDouble(); + } + + if (preset == DynamicPreset) + thr = (min_value_ + max_value_) * 0.5; // middle between min and max + + if ((int)preset == 1) thr = 0.9; + if ((int)preset == 2) thr = 1.8; + if ((int)preset == 3) thr = 2.5; + if ((int)preset == 4) thr = 1.5; + + result.push_back(thr); + } + + if (conv_type == A2LConversionBySchmittTrigger) { + double thr_lo = 0, thr_hi = 0; + + if (preset == NoPreset) { + auto thr_lo_iter = conversion_options_.find("threshold_value_low"); + if (thr_lo_iter != conversion_options_.end()) + thr_lo = (thr_lo_iter->second).toDouble(); + + auto thr_hi_iter = conversion_options_.find("threshold_value_high"); + if (thr_hi_iter != conversion_options_.end()) + thr_hi = (thr_hi_iter->second).toDouble(); + } + + if (preset == DynamicPreset) { + const double amplitude = max_value_ - min_value_; + const double center = min_value_ + (amplitude / 2); + thr_lo = center - (amplitude * 0.15); // 15% margin + thr_hi = center + (amplitude * 0.15); // 15% margin + } + + if ((int)preset == 1) { thr_lo = 0.3; thr_hi = 1.2; } + if ((int)preset == 2) { thr_lo = 0.7; thr_hi = 2.5; } + if ((int)preset == 3) { thr_lo = 1.3; thr_hi = 3.7; } + if ((int)preset == 4) { thr_lo = 0.8; thr_hi = 2.0; } + + result.push_back(thr_lo); + result.push_back(thr_hi); + } + + return result; +} + +vector< pair > SignalBase::get_conversion_presets() const +{ + vector< pair > presets; + + if (conversion_type_ == A2LConversionByThreshold) { + // Source: http://www.interfacebus.com/voltage_threshold.html + presets.emplace_back(tr("Signal average"), 0); + presets.emplace_back(tr("0.9V (for 1.8V CMOS)"), 1); + presets.emplace_back(tr("1.8V (for 3.3V CMOS)"), 2); + presets.emplace_back(tr("2.5V (for 5.0V CMOS)"), 3); + presets.emplace_back(tr("1.5V (for TTL)"), 4); + } + + if (conversion_type_ == A2LConversionBySchmittTrigger) { + // Source: http://www.interfacebus.com/voltage_threshold.html + presets.emplace_back(tr("Signal average +/- 15%"), 0); + presets.emplace_back(tr("0.3V/1.2V (for 1.8V CMOS)"), 1); + presets.emplace_back(tr("0.7V/2.5V (for 3.3V CMOS)"), 2); + presets.emplace_back(tr("1.3V/3.7V (for 5.0V CMOS)"), 3); + presets.emplace_back(tr("0.8V/2.0V (for TTL)"), 4); + } + + return presets; +} + +SignalBase::ConversionPreset SignalBase::get_current_conversion_preset() const +{ + auto preset = conversion_options_.find("preset"); + if (preset != conversion_options_.end()) + return (ConversionPreset)((preset->second).toInt()); + + return NoPreset; +} + +void SignalBase::set_conversion_preset(ConversionPreset id) +{ + conversion_options_["preset"] = (int)id; +} + #ifdef ENABLE_DECODE bool SignalBase::is_decode_signal() const { @@ -211,6 +365,14 @@ void SignalBase::save_settings(QSettings &settings) const settings.setValue("enabled", enabled()); settings.setValue("colour", colour()); settings.setValue("conversion_type", (int)conversion_type_); + + settings.setValue("conv_options", (int)(conversion_options_.size())); + int i = 0; + for (auto kvp : conversion_options_) { + settings.setValue(QString("conv_option%1_key").arg(i), kvp.first); + settings.setValue(QString("conv_option%1_value").arg(i), kvp.second); + i++; + } } void SignalBase::restore_settings(QSettings &settings) @@ -219,12 +381,21 @@ void SignalBase::restore_settings(QSettings &settings) set_enabled(settings.value("enabled").toBool()); set_colour(settings.value("colour").value()); set_conversion_type((ConversionType)settings.value("conversion_type").toInt()); + + int conv_options = settings.value("conv_options").toInt(); + + if (conv_options) + for (int i = 0; i < conv_options; i++) { + QString key = settings.value(QString("conv_option%1_key").arg(i)).toString(); + QVariant value = settings.value(QString("conv_option%1_value").arg(i)); + conversion_options_[key] = value; + } } bool SignalBase::conversion_is_a2l() const { return ((channel_type_ == AnalogChannel) && - ((conversion_type_ == A2LConversionByTreshold) || + ((conversion_type_ == A2LConversionByThreshold) || (conversion_type_ == A2LConversionBySchmittTrigger))); } @@ -255,8 +426,7 @@ void SignalBase::conversion_thread_proc(QObject* segment) end_sample = asegment->get_sample_count(); if (end_sample > start_sample) { - float min_v, max_v; - tie(min_v, max_v) = asegment->get_min_max(); + tie(min_value_, max_value_) = asegment->get_min_max(); // Create sigrok::Analog instance float *asamples = new float[ConversionBlockSize]; @@ -279,8 +449,8 @@ void SignalBase::conversion_thread_proc(QObject* segment) // Convert 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_ == A2LConversionByThreshold) { + const double threshold = get_conversion_thresholds()[0]; // Convert as many sample blocks as we can while ((end_sample - i) > ConversionBlockSize) { @@ -309,10 +479,10 @@ void SignalBase::conversion_thread_proc(QObject* segment) } 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 + const vector thresholds = get_conversion_thresholds(); + const double lo_thr = thresholds[0]; + const double hi_thr = thresholds[1]; + uint8_t state = 0; // TODO Use value of logic sample n-1 instead of 0 // Convert as many sample blocks as we can @@ -358,10 +528,18 @@ void SignalBase::conversion_thread_proc(QObject* segment) } while (!conversion_interrupt_); } -void SignalBase::start_conversion() +void SignalBase::start_conversion(bool delayed_start) { + if (delayed_start) { + delayed_conversion_starter_.start(); + return; + } + stop_conversion(); + if (converted_data_) + converted_data_->clear(); + if (conversion_is_a2l()) { shared_ptr analog_data = dynamic_pointer_cast(data_); @@ -401,24 +579,38 @@ void SignalBase::on_samples_added(QObject* segment, uint64_t start_sample, // Notify the conversion thread since it's running conversion_input_cond_.notify_one(); } else { - // Start the conversion thread - start_conversion(); + // Start the conversion thread unless the delay timer is running + if (!delayed_conversion_starter_.isActive()) + start_conversion(); } } samples_added(segment, start_sample, end_sample); } +void SignalBase::on_min_max_changed(float min, float max) +{ + // Restart conversion if one is enabled and uses a calculated threshold + if ((conversion_type_ != NoConversion) && + (get_current_conversion_preset() == DynamicPreset)) + start_conversion(true); + + min_max_changed(min, max); +} + void SignalBase::on_capture_state_changed(int state) { if (state == Session::Running) { - if (conversion_type_ != NoConversion) { - // Restart conversion - stop_conversion(); + // Restart conversion if one is enabled + if (conversion_type_ != NoConversion) start_conversion(); - } } } +void SignalBase::on_delayed_conversion_start() +{ + start_conversion(); +} + } // namespace data } // namespace pv