PulseView  unreleased development snapshot
A Qt-based sigrok GUI
mathsignal.cpp
Go to the documentation of this file.
1 /*
2  * This file is part of the PulseView project.
3  *
4  * Copyright (C) 2020 Soeren Apel <soeren@apelpie.net>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #include <limits>
21 
22 #include <QDebug>
23 
24 #include "mathsignal.hpp"
25 
26 #include <extdef.h>
27 #include <pv/globalsettings.hpp>
28 #include <pv/session.hpp>
30 #include <pv/data/signalbase.hpp>
31 
32 using std::dynamic_pointer_cast;
33 using std::make_shared;
34 using std::min;
35 using std::unique_lock;
36 
37 namespace pv {
38 namespace data {
39 
40 #define MATH_ERR_NONE 0
41 #define MATH_ERR_EMPTY_EXPR 1
42 #define MATH_ERR_EXPRESSION 2
43 #define MATH_ERR_INVALID_SIGNAL 3
44 #define MATH_ERR_ENABLE 4
45 
46 const int64_t MathSignal::ChunkLength = 256 * 1024;
47 
48 
49 template<typename T>
51 {
56 
58  exprtk::igeneric_function<T>("ST"), // Require channel name and sample number
59  owner_(owner),
60  sig_data(nullptr)
61  {
62  }
63 
64  T operator()(parameter_list_t parameters)
65  {
66  const string_t exprtk_sig_name = string_t(parameters[0]);
67  const scalar_t exprtk_sample_num = scalar_t(parameters[1]);
68 
69  const std::string str_sig_name = to_str(exprtk_sig_name);
70  const double sample_num = exprtk_sample_num();
71 
72  if (sample_num < 0)
73  return 0;
74 
75  if (!sig_data)
76  sig_data = owner_.signal_from_name(str_sig_name);
77 
78  if (!sig_data)
79  // There doesn't actually exist a signal with that name
80  return 0;
81 
83 
84  return T(sig_data->sample_value);
85  }
86 
88  uint32_t current_segment;
90 };
91 
92 
94  SignalBase(nullptr, SignalBase::MathChannel),
95  session_(session),
96  use_custom_sample_rate_(false),
97  use_custom_sample_count_(false),
98  expression_(""),
99  error_type_(MATH_ERR_NONE),
100  exprtk_unknown_symbol_table_(nullptr),
101  exprtk_symbol_table_(nullptr),
102  exprtk_expression_(nullptr),
103  exprtk_parser_(nullptr),
104  fnc_sample_(nullptr)
105 {
106  uint32_t sig_idx = session_.get_next_signal_index(MathChannel);
107  set_name(QString(tr("Math%1")).arg(sig_idx));
109 
110  set_data(std::make_shared<data::Analog>());
111 
112  connect(&session_, SIGNAL(capture_state_changed(int)),
113  this, SLOT(on_capture_state_changed(int)));
114 }
115 
117 {
119 }
120 
122 {
123  SignalBase::save_settings(settings);
124 
125  settings.setValue("expression", expression_);
126 
127  settings.setValue("custom_sample_rate", (qulonglong)custom_sample_rate_);
128  settings.setValue("custom_sample_count", (qulonglong)custom_sample_count_);
129  settings.setValue("use_custom_sample_rate", use_custom_sample_rate_);
130  settings.setValue("use_custom_sample_count", use_custom_sample_count_);
131 }
132 
134 {
136 
137  if (settings.contains("expression"))
138  expression_ = settings.value("expression").toString();
139 
140  if (settings.contains("custom_sample_rate"))
141  custom_sample_rate_ = settings.value("custom_sample_rate").toULongLong();
142 
143  if (settings.contains("custom_sample_count"))
144  custom_sample_count_ = settings.value("custom_sample_count").toULongLong();
145 
146  if (settings.contains("use_custom_sample_rate"))
147  use_custom_sample_rate_ = settings.value("use_custom_sample_rate").toBool();
148 
149  if (settings.contains("use_custom_sample_count"))
150  use_custom_sample_count_ = settings.value("use_custom_sample_count").toBool();
151 }
152 
154 {
155  return expression_;
156 }
157 
159 {
161 
163 }
164 
165 void MathSignal::set_error(uint8_t type, QString msg)
166 {
167  error_type_ = type;
168  error_message_ = msg;
169  // TODO Emulate noquote()
170  qDebug().nospace() << name() << ": " << msg << "(Expression: '" + expression_ + "')";
171 
173 }
174 
175 uint64_t MathSignal::get_working_sample_count(uint32_t segment_id) const
176 {
177  // The working sample count is the highest sample number for
178  // which all used signals have data available, so go through all
179  // channels and use the lowest overall sample count of the segment
180 
181  int64_t result = std::numeric_limits<int64_t>::max();
182 
184  // A custom sample count implies that only one segment will be created
185  result = (segment_id == 0) ? custom_sample_count_ : 0;
186  else {
187  if (input_signals_.size() > 0) {
188  for (auto input_signal : input_signals_) {
189  const shared_ptr<SignalBase>& sb = input_signal.second.sb;
190 
191  shared_ptr<Analog> a = sb->analog_data();
192  auto analog_segments = a->analog_segments();
193 
194  if (analog_segments.size() == 0) {
195  result = 0;
196  continue;
197  }
198 
199  const uint32_t highest_segment_id = (analog_segments.size() - 1);
200  if (segment_id > highest_segment_id)
201  continue;
202 
203  const shared_ptr<AnalogSegment> segment = analog_segments.at(segment_id);
204  result = min(result, (int64_t)segment->get_sample_count());
205  }
206  } else
207  result = session_.get_segment_sample_count(segment_id);
208  }
209 
210  return result;
211 }
212 
213 void MathSignal::update_completeness(uint32_t segment_id, uint64_t output_sample_count)
214 {
215  bool output_complete = true;
216 
217  if (input_signals_.size() > 0) {
218  for (auto input_signal : input_signals_) {
219  const shared_ptr<SignalBase>& sb = input_signal.second.sb;
220 
221  shared_ptr<Analog> a = sb->analog_data();
222  auto analog_segments = a->analog_segments();
223 
224  if (analog_segments.size() == 0) {
225  output_complete = false;
226  continue;
227  }
228 
229  const uint32_t highest_segment_id = (analog_segments.size() - 1);
230  if (segment_id > highest_segment_id) {
231  output_complete = false;
232  continue;
233  }
234 
235  const shared_ptr<AnalogSegment> segment = analog_segments.at(segment_id);
236  if (!segment->is_complete()) {
237  output_complete = false;
238  continue;
239  }
240 
241  if (output_sample_count < segment->get_sample_count())
242  output_complete = false;
243  }
244  } else {
245  // We're done when we generated as many samples as the stopped session is long
247  (output_sample_count < session_.get_segment_sample_count(segment_id)))
248  output_complete = false;
249  }
250 
251  if (output_complete)
252  analog_data()->analog_segments().at(segment_id)->set_complete();
253 }
254 
256 {
257  if (gen_thread_.joinable()) {
258  gen_interrupt_ = true;
259  gen_input_cond_.notify_one();
260  gen_thread_.join();
261  }
262 
263  data_->clear();
264  input_signals_.clear();
265 
266  if (exprtk_parser_) {
267  delete exprtk_parser_;
268  exprtk_parser_ = nullptr;
269  }
270 
271  if (exprtk_expression_) {
272  delete exprtk_expression_;
273  exprtk_expression_ = nullptr;
274  }
275 
276  if (exprtk_symbol_table_) {
277  delete exprtk_symbol_table_;
278  exprtk_symbol_table_ = nullptr;
279  }
280 
284  }
285 
286  if (fnc_sample_) {
287  delete fnc_sample_;
288  fnc_sample_ = nullptr;
289  }
290 
291  if (!error_message_.isEmpty()) {
292  error_message_.clear();
294  // TODO Emulate noquote()
295  qDebug().nospace() << name() << ": Error cleared";
296  }
297 
299 }
300 
302 {
304 
305  if (expression_.isEmpty()) {
306  set_error(MATH_ERR_EMPTY_EXPR, tr("No expression defined, nothing to do"));
307  return;
308  }
309 
310  disconnect(&session_, SIGNAL(data_received()), this, SLOT(on_data_received()));
311 
312  for (const shared_ptr<SignalBase>& sb : session_.signalbases()) {
313  if (sb->analog_data())
314  disconnect(sb->analog_data().get(), nullptr, this, SLOT(on_data_received()));
315  disconnect(sb.get(), nullptr, this, SLOT(on_enabled_changed()));
316  }
317 
318  fnc_sample_ = new fnc_sample<double>(*this);
319 
321 
323  exprtk_symbol_table_->add_constant("T", 1 / session_.get_samplerate());
324  exprtk_symbol_table_->add_function("sample", *fnc_sample_);
325  exprtk_symbol_table_->add_variable("t", exprtk_current_time_);
326  exprtk_symbol_table_->add_variable("s", exprtk_current_sample_);
327  exprtk_symbol_table_->add_constants();
328 
331  exprtk_expression_->register_symbol_table(*exprtk_symbol_table_);
332 
335 
336  if (!exprtk_parser_->compile(expression_.toStdString(), *exprtk_expression_)) {
337  QString error_details;
338  size_t error_count = exprtk_parser_->error_count();
339 
340  for (size_t i = 0; i < error_count; i++) {
341  typedef exprtk::parser_error::type error_t;
342  error_t error = exprtk_parser_->get_error(i);
343  exprtk::parser_error::update_error(error, expression_.toStdString());
344 
345  QString error_detail = tr("%1 at line %2, column %3: %4");
346  if ((error_count > 1) && (i < (error_count - 1)))
347  error_detail += "\n";
348 
349  error_details += error_detail \
350  .arg(exprtk::parser_error::to_str(error.mode).c_str()) \
351  .arg(error.line_no) \
352  .arg(error.column_no) \
353  .arg(error.diagnostic.c_str());
354  }
355  set_error(MATH_ERR_EXPRESSION, error_details);
356  } else {
357  // Resolve unknown scalars to signals and add them to the input signal list
358  vector<string> unknowns;
360  for (string& unknown : unknowns) {
361  signal_data* sig_data = signal_from_name(unknown);
362  const shared_ptr<SignalBase> signal = (sig_data) ? (sig_data->sb) : nullptr;
363  if (!signal || (!signal->analog_data())) {
364  set_error(MATH_ERR_INVALID_SIGNAL, QString(tr("\"%1\" isn't a valid analog signal")) \
365  .arg(QString::fromStdString(unknown)));
366  } else
367  sig_data->ref = &(exprtk_unknown_symbol_table_->variable_ref(unknown));
368  }
369  }
370 
371  QString disabled_signals;
372  if (!all_input_signals_enabled(disabled_signals) && error_message_.isEmpty())
374  tr("No data will be generated as %1 must be enabled").arg(disabled_signals));
375 
376  if (error_message_.isEmpty()) {
377  // Connect to the session data notification if we have no input signals
378  if (input_signals_.empty())
379  connect(&session_, SIGNAL(data_received()), this, SLOT(on_data_received()));
380 
381  gen_interrupt_ = false;
382  gen_thread_ = std::thread(&MathSignal::generation_proc, this);
383  }
384 }
385 
386 uint64_t MathSignal::generate_samples(uint32_t segment_id, const uint64_t start_sample,
387  const int64_t sample_count)
388 {
389  uint64_t count = 0;
390 
391  shared_ptr<Analog> analog = dynamic_pointer_cast<Analog>(data_);
392  shared_ptr<AnalogSegment> segment = analog->analog_segments().at(segment_id);
393 
394  // Keep the math functions segment IDs in sync
395  fnc_sample_->current_segment = segment_id;
396 
397  const double sample_rate = data_->get_samplerate();
398 
399  exprtk_current_sample_ = start_sample;
400 
401  float *sample_data = new float[sample_count];
402 
403  for (int64_t i = 0; i < sample_count; i++) {
405 
406  for (auto& entry : input_signals_) {
407  signal_data* sig_data = &(entry.second);
408  update_signal_sample(sig_data, segment_id, exprtk_current_sample_);
409  }
410 
411  double value = exprtk_expression_->value();
412  sample_data[i] = value;
414  count++;
415 
416  // If during the evaluation of the expression it was found that this
417  // math signal itself is being accessed, the chunk size was reduced
418  // to 1, which means we must stop after this sample we just generated
419  if (generation_chunk_size_ == 1)
420  break;
421  }
422 
423  segment->append_interleaved_samples(sample_data, count, 1);
424 
425  delete[] sample_data;
426 
427  return count;
428 }
429 
431 {
432  // Don't do anything until we have a valid sample rate
433  do {
435  data_->set_samplerate(custom_sample_rate_);
436  else
437  data_->set_samplerate(session_.get_samplerate());
438 
439  if (data_->get_samplerate() == 1) {
440  unique_lock<mutex> gen_input_lock(input_mutex_);
441  gen_input_cond_.wait(gen_input_lock);
442  }
443  } while ((!gen_interrupt_) && (data_->get_samplerate() == 1));
444 
445  if (gen_interrupt_)
446  return;
447 
448  uint32_t segment_id = 0;
449  shared_ptr<Analog> analog = analog_data();
450 
451  // Create initial analog segment
452  shared_ptr<AnalogSegment> output_segment =
453  make_shared<AnalogSegment>(*analog.get(), segment_id, analog->get_samplerate());
454  analog->push_segment(output_segment);
455 
456  // Create analog samples
457  do {
458  const uint64_t input_sample_count = get_working_sample_count(segment_id);
459  const uint64_t output_sample_count = output_segment->get_sample_count();
460 
461  const uint64_t samples_to_process =
462  (input_sample_count > output_sample_count) ?
463  (input_sample_count - output_sample_count) : 0;
464 
465  // Process the samples if necessary...
466  if (samples_to_process > 0) {
467  uint64_t processed_samples = 0;
468  do {
469  const uint64_t start_sample = output_sample_count + processed_samples;
470  uint64_t sample_count =
471  min(samples_to_process - processed_samples, generation_chunk_size_);
472 
473  sample_count = generate_samples(segment_id, start_sample, sample_count);
474  processed_samples += sample_count;
475 
476  // Notify consumers of this signal's data
477  samples_added(segment_id, start_sample, start_sample + processed_samples);
478  } while (!gen_interrupt_ && (processed_samples < samples_to_process));
479  }
480 
481  update_completeness(segment_id, output_sample_count);
482 
483  if (output_segment->is_complete() && (segment_id < session_.get_highest_segment_id())) {
484  // Process next segment
485  segment_id++;
486 
487  output_segment =
488  make_shared<AnalogSegment>(*analog.get(), segment_id, analog->get_samplerate());
489  analog->push_segment(output_segment);
490  }
491 
492  if (!gen_interrupt_ && (samples_to_process == 0)) {
493  // Wait for more input
494  unique_lock<mutex> gen_input_lock(input_mutex_);
495  gen_input_cond_.wait(gen_input_lock);
496  }
497  } while (!gen_interrupt_);
498 }
499 
501 {
502  // If the expression contains the math signal itself, we must add every sample to
503  // the output segment immediately so that it can be accessed
504  const QString sig_name = QString::fromStdString(name);
505  if (sig_name == this->name())
507 
508  // Look up signal in the map and if it doesn't exist yet, add it for future use
509 
510  auto element = input_signals_.find(name);
511 
512  if (element != input_signals_.end()) {
513  return &(element->second);
514  } else {
515  const vector< shared_ptr<SignalBase> > signalbases = session_.signalbases();
516 
517  for (const shared_ptr<SignalBase>& sb : signalbases)
518  if (sb->name() == sig_name) {
519  if (!sb->analog_data())
520  continue;
521 
522  connect(sb->analog_data().get(), SIGNAL(samples_added(SharedPtrToSegment, uint64_t, uint64_t)),
523  this, SLOT(on_data_received()));
524  connect(sb->analog_data().get(), SIGNAL(segment_completed()),
525  this, SLOT(on_data_received()));
526 
527  connect(sb.get(), SIGNAL(enabled_changed(bool)),
528  this, SLOT(on_enabled_changed()));
529 
530  return &(input_signals_.insert({name, signal_data(sb)}).first->second);
531  }
532  }
533 
534  // If we reach this point, no valid signal was found with the supplied name
535  if (error_type_ == MATH_ERR_NONE)
536  set_error(MATH_ERR_INVALID_SIGNAL, QString(tr("\"%1\" isn't a valid analog signal")) \
537  .arg(QString::fromStdString(name)));
538 
539  return nullptr;
540 }
541 
542 void MathSignal::update_signal_sample(signal_data* sig_data, uint32_t segment_id, uint64_t sample_num)
543 {
544  assert(sig_data);
545 
546  // Update the value only if a different sample is requested
547  if (sig_data->sample_num == sample_num)
548  return;
549 
550  assert(sig_data->sb);
551  const shared_ptr<pv::data::Analog> analog = sig_data->sb->analog_data();
552  assert(analog);
553 
554  assert(segment_id < analog->analog_segments().size());
555 
556  const shared_ptr<AnalogSegment> segment = analog->analog_segments().at(segment_id);
557 
558  sig_data->sample_num = sample_num;
559 
560  if (sample_num < segment->get_sample_count())
561  sig_data->sample_value = segment->get_sample(sample_num);
562  else
563  sig_data->sample_value = 0;
564 
565  // We only have a reference if this signal is used as a scalar;
566  // if it's used by a function, it's null
567  if (sig_data->ref)
568  *(sig_data->ref) = sig_data->sample_value;
569 }
570 
571 bool MathSignal::all_input_signals_enabled(QString &disabled_signals) const
572 {
573  bool all_enabled = true;
574 
575  disabled_signals.clear();
576 
577  for (auto input_signal : input_signals_) {
578  const shared_ptr<SignalBase>& sb = input_signal.second.sb;
579 
580  if (!sb->enabled()) {
581  all_enabled = false;
582  disabled_signals += disabled_signals.isEmpty() ?
583  sb->name() : ", " + sb->name();
584  }
585  }
586 
587  return all_enabled;
588 }
589 
591 {
592  if (state == Session::Running)
594 
595  // Make sure we don't miss any input samples, just in case
596  if (state == Session::Stopped)
597  gen_input_cond_.notify_one();
598 }
599 
601 {
602  gen_input_cond_.notify_one();
603 }
604 
606 {
607  QString disabled_signals;
608  if (!all_input_signals_enabled(disabled_signals) &&
611  tr("No data will be generated as %1 must be enabled").arg(disabled_signals));
612  else if (disabled_signals.isEmpty() && (error_type_ == MATH_ERR_ENABLE)) {
614  error_message_.clear();
615  }
616 }
617 
618 } // namespace data
619 } // namespace pv
bool update_error(type &error, const std::string &expression)
Definition: exprtk.hpp:18243
void register_symbol_table(symbol_table< T > &st)
Definition: exprtk.hpp:17983
#define MATH_ERR_INVALID_SIGNAL
Definition: mathsignal.cpp:43
exprtk::symbol_table< double > * exprtk_unknown_symbol_table_
Definition: mathsignal.hpp:135
exprtk::parser< double > * exprtk_parser_
Definition: mathsignal.hpp:137
const vector< shared_ptr< data::SignalBase > > signalbases() const
Definition: session.cpp:957
igeneric_function(const std::string &param_seq="", const return_type rtr_type=e_rtrn_scalar)
Definition: exprtk.hpp:16104
void error_message_changed(QString msg)
signal_data * signal_from_name(const std::string &name)
Definition: mathsignal.cpp:500
void set_data(shared_ptr< pv::data::SignalData > data)
Definition: signalbase.cpp:271
bool compile(const std::string &expression_string, expression< T > &expr)
Definition: exprtk.hpp:20173
std::string to_str(int i)
Definition: exprtk.hpp:263
std::shared_ptr< pv::data::Segment > SharedPtrToSegment
Definition: segment.hpp:131
uint32_t get_next_signal_index(data::SignalBase::ChannelType type)
Definition: session.cpp:968
shared_ptr< pv::data::SignalData > data_
Definition: signalbase.hpp:409
void set_error(uint8_t type, QString msg)
Definition: mathsignal.cpp:165
const shared_ptr< SignalBase > sb
Definition: mathsignal.hpp:57
void on_capture_state_changed(int state)
Definition: mathsignal.cpp:590
#define MATH_ERR_NONE
Definition: mathsignal.cpp:40
T value(details::expression_node< T > *n)
Definition: exprtk.hpp:12358
std::size_t get_variable_list(Sequence< std::pair< std::string, T >, Allocator > &vlist) const
Definition: exprtk.hpp:17450
pv::Session & session_
Definition: mathsignal.hpp:117
uint64_t custom_sample_count_
Definition: mathsignal.hpp:120
uint32_t current_segment
Definition: mathsignal.cpp:88
std::size_t error_count() const
Definition: exprtk.hpp:20447
condition_variable gen_input_cond_
Definition: mathsignal.hpp:130
ChannelType type() const
Definition: signalbase.cpp:177
void set_expression(QString expression)
Definition: mathsignal.cpp:158
exprtk::symbol_table< double > * exprtk_symbol_table_
Definition: mathsignal.hpp:135
void update_completeness(uint32_t segment_id, uint64_t output_sample_count)
Definition: mathsignal.cpp:213
#define MATH_ERR_EMPTY_EXPR
Definition: mathsignal.cpp:41
virtual void restore_settings(QSettings &settings)
Definition: mathsignal.cpp:133
Virtual channel generated by math operations.
Definition: signalbase.hpp:97
parser_error::type get_error(const std::size_t &index) const
Definition: exprtk.hpp:20429
virtual void save_settings(QSettings &settings) const
Definition: signalbase.cpp:565
T max(const T v0, const T v1)
Definition: exprtk.hpp:1411
atomic< bool > gen_interrupt_
Definition: mathsignal.hpp:133
exprtk::igeneric_function< T >::parameter_list_t parameter_list_t
Definition: mathsignal.cpp:52
bool all_input_signals_enabled(QString &disabled_signals) const
Definition: mathsignal.cpp:571
uint32_t get_highest_segment_id() const
Definition: session.cpp:933
static std::string data()
Definition: exprtk.hpp:39024
double get_samplerate() const
Definition: session.cpp:910
map< std::string, signal_data > input_signals_
Definition: mathsignal.hpp:123
virtual void set_name(QString name)
Definition: signalbase.cpp:236
uint64_t generation_chunk_size_
Definition: mathsignal.hpp:122
bool add_variable(const std::string &variable_name, T &t, const bool is_constant=false)
Definition: exprtk.hpp:17127
virtual void save_settings(QSettings &settings) const
Definition: mathsignal.cpp:121
QString get_expression() const
Definition: mathsignal.cpp:153
signal_data * sig_data
Definition: mathsignal.cpp:89
void enabled_changed(const bool &value)
T min(const T v0, const T v1)
Definition: exprtk.hpp:1404
capture_state get_capture_state() const
Definition: session.cpp:788
const deque< shared_ptr< AnalogSegment > > & analog_segments() const
Definition: analog.cpp:49
uint64_t get_segment_sample_count(uint32_t segment_id) const
Definition: session.cpp:938
exprtk::expression< double > * exprtk_expression_
Definition: mathsignal.hpp:136
bool add_constant(const std::string &constant_name, const T &value)
Definition: exprtk.hpp:17139
T value() const
Definition: exprtk.hpp:17963
#define countof(x)
Definition: extdef.h:23
fnc_sample< double > * fnc_sample_
Definition: mathsignal.hpp:140
T operator()(parameter_list_t parameters)
Definition: mathsignal.cpp:64
uint64_t generate_samples(uint32_t segment_id, const uint64_t start_sample, const int64_t sample_count)
Definition: mathsignal.cpp:386
bool add_function(const std::string &function_name, function_t &function)
Definition: exprtk.hpp:17168
void update_signal_sample(signal_data *sig_data, uint32_t segment_id, uint64_t sample_num)
Definition: mathsignal.cpp:542
virtual void begin_generation()
Definition: mathsignal.cpp:301
#define MATH_ERR_EXPRESSION
Definition: mathsignal.cpp:42
MathSignal & owner_
Definition: mathsignal.cpp:87
shared_ptr< pv::data::Analog > analog_data() const
Definition: signalbase.cpp:309
virtual void restore_settings(QSettings &settings)
Definition: signalbase.cpp:581
#define MATH_ERR_ENABLE
Definition: mathsignal.cpp:44
std::thread gen_thread_
Definition: mathsignal.hpp:132
MathSignal(pv::Session &session)
Definition: mathsignal.cpp:93
void enable_unknown_symbol_resolver(unknown_symbol_resolver *usr=reinterpret_cast< unknown_symbol_resolver * >(0))
Definition: exprtk.hpp:20477
static const QColor AnalogSignalColors[8]
Definition: signalbase.hpp:116
QString name() const
Definition: signalbase.cpp:210
uint64_t custom_sample_rate_
Definition: mathsignal.hpp:119
virtual void set_color(QColor color)
Definition: signalbase.cpp:251
generic_type::string_view string_t
Definition: mathsignal.cpp:55
fnc_sample(MathSignal &owner)
Definition: mathsignal.cpp:57
T & variable_ref(const std::string &symbol_name)
Definition: exprtk.hpp:17047
static const int64_t ChunkLength
Definition: mathsignal.hpp:69
exprtk::igeneric_function< T >::generic_type generic_type
Definition: mathsignal.cpp:53
generic_type::scalar_view scalar_t
Definition: mathsignal.cpp:54
void samples_added(uint64_t segment_id, uint64_t start_sample, uint64_t end_sample)
std::string to_str(error_mode mode)
Definition: exprtk.hpp:18228
uint64_t get_working_sample_count(uint32_t segment_id) const
Definition: mathsignal.cpp:175