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 <extdef.h>
21 
22 #include <QComboBox>
23 #include <QDebug>
24 #include <QDialogButtonBox>
25 #include <QFormLayout>
26 #include <QGridLayout>
27 #include <QLabel>
28 #include <QLineEdit>
29 #include <QPushButton>
30 #include <QString>
31 #include <QTabWidget>
32 #include <QVBoxLayout>
33 
34 #include "mathsignal.hpp"
35 
36 #include "pv/data/signalbase.hpp"
37 
39 
40 namespace pv {
41 namespace views {
42 namespace trace {
43 
44 #define MATHSIGNAL_INPUT_TIMEOUT (2000)
45 
46 const vector< pair<string, string> > MathEditDialog::Examples = {
47  {"Product of signals named A1 and A2:",
48  "A1 * A2"},
49  {"Create a sine wave with f=1MHz:",
50  "sin(1e6 * 2 * pi * t)"},
51  {"Average the past 4 samples of A2:",
52  "var v := 0;\n" \
53  "var n := 4;\n\n" \
54  "for (var i := 0; i < n; i += 1) {\n" \
55  "\tv += sample('A2', s - i) / n;\n" \
56  "}"},
57  {"Create a <a href=https://en.wikipedia.org/wiki/Chirp#Linear>frequency sweep</a> from 1Hz to 1MHz over 10 seconds:",
58  "var f_min := 1;\n" \
59  "var f_max := 1e6;\n" \
60  "var duration := 10;\n" \
61  "var gradient := (f_max - f_min) / duration;\n" \
62  "// phase is the antiderivative of (gradient * t + f_min) dt\n" \
63  "var phase := gradient * (t ^ 2 / 2) + f_min * t;\n" \
64  "sin(2 * pi * phase)"},
65  {"Single-pole low-pass IIR filter, see " \
66  "<a href=https://tomroelandts.com/articles/low-pass-single-pole-iir-filter>this</a> " \
67  "and <a href=https://dsp.stackexchange.com/questions/34969/cutoff-frequency-of-a-first-order-recursive-filter>this</a> article:",
68  "// -3db point is set to 10% of the sampling frequency\n" \
69  "var f_c := 0.1;\n" \
70  "var omega_c := 2 * pi * f_c;\n" \
71  "var b := 2 - cos(omega_c) - sqrt(((2 - cos(omega_c)) ^ 2) - 1);\n" \
72  "var a := 1 - b;\n\n" \
73  "// formula is y[n] = ax[n] + (1 - a) * y[n - 1]\n" \
74  "// x[n] becomes the input signal, here A4\n" \
75  "// y[n - 1] becomes sample('Math1', s - 1)\n" \
76  "a * A4 + (1 - a) * sample('Math1', s - 1)"}
77 };
78 
79 
81  shared_ptr<pv::data::MathSignal> math_signal, QWidget *parent) :
82  QDialog(parent),
83  session_(session),
84  math_signal_(math_signal),
85  old_expression_(math_signal->get_expression()),
86  expr_edit_(new QPlainTextEdit())
87 {
88  setWindowTitle(tr("Math Expression Editor"));
89 
90  // Create the pages
91  QWidget *basics_page = new QWidget();
92  QVBoxLayout *basics_layout = new QVBoxLayout(basics_page);
93  basics_layout->addWidget(new QLabel("<b>" + tr("Inputs:") + "</b>"));
94  basics_layout->addWidget(new QLabel("signal_name\tCurrent value of signal signal_name (e.g. A0 or CH1)"));
95  basics_layout->addWidget(new QLabel("T\t\tSampling interval (= 1 / sample rate)"));
96  basics_layout->addWidget(new QLabel("t\t\tCurrent time in seconds"));
97  basics_layout->addWidget(new QLabel("s\t\tCurrent sample number"));
98  basics_layout->addWidget(new QLabel("sample('s', n)\tValue of sample #n of the signal named s"));
99  basics_layout->addWidget(new QLabel("<b>" + tr("Variables:") + "</b>"));
100  basics_layout->addWidget(new QLabel("var x;\t\tCreate variable\nvar x := 5;\tCreate variable with initial value"));
101  basics_layout->addWidget(new QLabel("<b>" + tr("Basic operators:") + "</b>"));
102  basics_layout->addWidget(new QLabel("x + y\t\tAddition of x and y"));
103  basics_layout->addWidget(new QLabel("x - y\t\tSubtraction of x and y"));
104  basics_layout->addWidget(new QLabel("x * y\t\tMultiplication of x and y"));
105  basics_layout->addWidget(new QLabel("x / y\t\tDivision of x and y"));
106  basics_layout->addWidget(new QLabel("x % y\t\tRemainder of division x / y"));
107  basics_layout->addWidget(new QLabel("x ^ y\t\tx to the power of y"));
108  basics_layout->addWidget(new QLabel("<b>" + tr("Assignments:") + "</b>"));
109  basics_layout->addWidget(new QLabel("x := y\t\tAssign the value of y to x"));
110  basics_layout->addWidget(new QLabel("x += y\t\tIncrement x by y"));
111  basics_layout->addWidget(new QLabel("x -= y\t\tDecrement x by y"));
112  basics_layout->addWidget(new QLabel("x *= y\t\tMultiply x by y"));
113  basics_layout->addWidget(new QLabel("x /= y\t\tDivide x by y"));
114  basics_layout->addWidget(new QLabel("x %= y\t\tSet x to the value of x % y"));
115 
116  QWidget *func1_page = new QWidget();
117  QVBoxLayout *func1_layout = new QVBoxLayout(func1_page);
118  func1_layout->addWidget(new QLabel("<b>" + tr("General purpose functions:") + "</b>"));
119  func1_layout->addWidget(new QLabel(tr("abs(x)\t\tAbsolute value of x")));
120  func1_layout->addWidget(new QLabel(tr("avg(x, y, ...)\tAverage of all input values")));
121  func1_layout->addWidget(new QLabel(tr("ceil(x)\t\tSmallest integer that is greater than or equal to x")));
122  func1_layout->addWidget(new QLabel(tr("clamp(lb, x, ub)\tClamp x in range between lb and ub, where lb < ub")));
123  func1_layout->addWidget(new QLabel(tr("equal(x, y)\tEquality test between x and y using normalised epsilon")));
124  func1_layout->addWidget(new QLabel(tr("erf(x)\t\tError function of x")));
125  func1_layout->addWidget(new QLabel(tr("erfc(x)\t\tComplimentary error function of x")));
126  func1_layout->addWidget(new QLabel(tr("exp(x)\t\te to the power of x")));
127  func1_layout->addWidget(new QLabel(tr("expm1(x)\te to the power of x minus 1, where x is very small.")));
128  func1_layout->addWidget(new QLabel(tr("floor(x)\t\tLargest integer that is less than or equal to x")));
129  func1_layout->addWidget(new QLabel(tr("frac(x)\t\tFractional portion of x")));
130  func1_layout->addWidget(new QLabel(tr("hypot(x)\t\tHypotenuse of x and y (i.e. sqrt(x*x + y*y))")));
131  func1_layout->addWidget(new QLabel(tr("iclamp(lb, x, ub)\tInverse-clamp x outside of the range lb and ub, where lb < ub.\n\t\tIf x is within the range it will snap to the closest bound")));
132  func1_layout->addWidget(new QLabel(tr("inrange(lb, x, ub)\tIn-range returns true when x is within the range lb and ub, where lb < ub.")));
133  func1_layout->addWidget(new QLabel(tr("log(x)\t\tNatural logarithm of x")));
134  func1_layout->addWidget(new QLabel(tr("log10(x)\t\tBase 10 logarithm of x")));
135 
136  QWidget *func2_page = new QWidget();
137  QVBoxLayout *func2_layout = new QVBoxLayout(func2_page);
138  func2_layout->addWidget(new QLabel(tr("log1p(x)\t\tNatural logarithm of 1 + x, where x is very small")));
139  func2_layout->addWidget(new QLabel(tr("log2(x)\t\tBase 2 logarithm of x")));
140  func2_layout->addWidget(new QLabel(tr("logn(x)\t\tBase N logarithm of x, where n is a positive integer")));
141  func2_layout->addWidget(new QLabel(tr("max(x, y, ...)\tLargest value of all the inputs")));
142  func2_layout->addWidget(new QLabel(tr("min(x, y, ...)\tSmallest value of all the inputs")));
143  func2_layout->addWidget(new QLabel(tr("mul(x, y, ...)\tProduct of all the inputs")));
144  func2_layout->addWidget(new QLabel(tr("ncdf(x)\t\tNormal cumulative distribution function")));
145  func2_layout->addWidget(new QLabel(tr("nequal(x, y)\tNot-equal test between x and y using normalised epsilon")));
146  func2_layout->addWidget(new QLabel(tr("pow(x, y)\tx to the power of y")));
147  func2_layout->addWidget(new QLabel(tr("root(x, n)\tNth-Root of x, where n is a positive integer")));
148  func2_layout->addWidget(new QLabel(tr("round(x)\t\tRound x to the nearest integer")));
149  func2_layout->addWidget(new QLabel(tr("roundn(x, n)\tRound x to n decimal places, where n > 0 and is an integer")));
150  func2_layout->addWidget(new QLabel(tr("sgn(x)\t\tSign of x; -1 if x < 0, +1 if x > 0, else zero")));
151  func2_layout->addWidget(new QLabel(tr("sqrt(x)\t\tSquare root of x, where x >= 0")));
152  func2_layout->addWidget(new QLabel(tr("sum(x, y, ..,)\tSum of all the inputs")));
153  func2_layout->addWidget(new QLabel(tr("swap(x, y)\tSwap the values of the variables x and y and return the current value of y")));
154  func2_layout->addWidget(new QLabel(tr("trunc(x)\t\tInteger portion of x")));
155 
156  QWidget *trig_page = new QWidget();
157  QVBoxLayout *trig_layout = new QVBoxLayout(trig_page);
158  trig_layout->addWidget(new QLabel("<b>" + tr("Trigonometry functions:") + "</b>"));
159  trig_layout->addWidget(new QLabel(tr("acos(x)\t\tArc cosine of x expressed in radians. Interval [-1,+1]")));
160  trig_layout->addWidget(new QLabel(tr("acosh(x)\t\tInverse hyperbolic cosine of x expressed in radians")));
161  trig_layout->addWidget(new QLabel(tr("asin(x)\t\tArc sine of x expressed in radians. Interval [-1,+1]")));
162  trig_layout->addWidget(new QLabel(tr("asinh(x)\t\tInverse hyperbolic sine of x expressed in radians")));
163  trig_layout->addWidget(new QLabel(tr("atan(x)\t\tArc tangent of x expressed in radians. Interval [-1,+1]")));
164  trig_layout->addWidget(new QLabel(tr("atan2(x, y)\tArc tangent of (x / y) expressed in radians. [-pi,+pi] ")));
165  trig_layout->addWidget(new QLabel(tr("atanh(x)\t\tInverse hyperbolic tangent of x expressed in radians")));
166  trig_layout->addWidget(new QLabel(tr("cos(x)\t\tCosine of x")));
167  trig_layout->addWidget(new QLabel(tr("cosh(x)\t\tHyperbolic cosine of x")));
168  trig_layout->addWidget(new QLabel(tr("cot(x)\t\tCotangent of x")));
169  trig_layout->addWidget(new QLabel(tr("csc(x)\t\tCosectant of x")));
170  trig_layout->addWidget(new QLabel(tr("sec(x)\t\tSecant of x")));
171  trig_layout->addWidget(new QLabel(tr("sin(x)\t\tSine of x")));
172  trig_layout->addWidget(new QLabel(tr("sinc(x)\t\tSine cardinal of x")));
173  trig_layout->addWidget(new QLabel(tr("sinh(x)\t\tHyperbolic sine of x")));
174  trig_layout->addWidget(new QLabel(tr("tan(x)\t\tTangent of x")));
175  trig_layout->addWidget(new QLabel(tr("tanh(x)\t\tHyperbolic tangent of x")));
176  trig_layout->addWidget(new QLabel(tr("deg2rad(x)\tConvert x from degrees to radians")));
177  trig_layout->addWidget(new QLabel(tr("deg2grad(x)\tConvert x from degrees to gradians")));
178  trig_layout->addWidget(new QLabel(tr("rad2deg(x)\tConvert x from radians to degrees")));
179  trig_layout->addWidget(new QLabel(tr("grad2deg(x)\tConvert x from gradians to degrees")));
180 
181  QWidget *logic_page = new QWidget();
182  QVBoxLayout *logic_layout = new QVBoxLayout(logic_page);
183  logic_layout->addWidget(new QLabel("<b>" + tr("Logic operators:") + "</b>"));
184  logic_layout->addWidget(new QLabel("true\t\tTrue state or any value other than zero (typically 1)"));
185  logic_layout->addWidget(new QLabel("false\t\tFalse state, value of exactly zero"));
186  logic_layout->addWidget(new QLabel("x and y\t\tLogical AND, true only if x and y are both true"));
187  logic_layout->addWidget(new QLabel("mand(x, y, z, ...)\tMulti-input logical AND, true only if all inputs are true"));
188  logic_layout->addWidget(new QLabel("x or y\t\tLogical OR, true if either x or y is true"));
189  logic_layout->addWidget(new QLabel("mor(x, y, z, ...)\tMulti-input logical OR, true if at least one of the inputs is true"));
190  logic_layout->addWidget(new QLabel("x nand y\t\tLogical NAND, true only if either x or y is false"));
191  logic_layout->addWidget(new QLabel("x nor y\t\tLogical NOR, true only if the result of x or y is false"));
192  logic_layout->addWidget(new QLabel("not(x)\t\tLogical NOT, negate the logical sense of the input"));
193  logic_layout->addWidget(new QLabel("x xor y\t\tLogical NOR, true only if the logical states of x and y differ"));
194  logic_layout->addWidget(new QLabel("x xnor y\t\tLogical NOR, true only if x and y have the same state"));
195  logic_layout->addWidget(new QLabel("x & y\t\tSame as AND but with left to right expression short circuiting optimisation"));
196  logic_layout->addWidget(new QLabel("x | y\t\tSame as OR but with left to right expression short circuiting optimisation"));
197 
198  QWidget *control1_page = new QWidget();
199  QVBoxLayout *control1_layout = new QVBoxLayout(control1_page);
200  control1_layout->addWidget(new QLabel("<b>" + tr("Comparisons:") + "</b>"));
201  control1_layout->addWidget(new QLabel(tr("x = y or x == y\tTrue only if x is strictly equal to y")));
202  control1_layout->addWidget(new QLabel(tr("x <> y or x != y\tTrue only if x does not equal y")));
203  control1_layout->addWidget(new QLabel(tr("x < y\t\tTrue only if x is less than y")));
204  control1_layout->addWidget(new QLabel(tr("x <= y\t\tTrue only if x is less than or equal to y")));
205  control1_layout->addWidget(new QLabel(tr("x > y\t\tTrue only if x is greater than y")));
206  control1_layout->addWidget(new QLabel(tr("x >= y\t\tTrue only if x is greater than or equal to y")));
207  control1_layout->addWidget(new QLabel("<b>" + tr("Flow control:") + "</b>"));
208  control1_layout->addWidget(new QLabel(tr("{ ... }\t\tBeginning and end of instruction block")));
209  control1_layout->addWidget(new QLabel(tr("if (x, y, z)\tIf x is true then return y else return z\nif (x) y;\t\t\tvariant without implied else\nif (x) { y };\t\tvariant with an instruction block\nif (x) y; else z;\t\tvariant with explicit else\nif (x) { y } else { z };\tvariant with instruction blocks")));
210  control1_layout->addWidget(new QLabel(tr("x ? y : z\tTernary operator, equivalent to 'if (x, y, z)'")));
211  control1_layout->addWidget(new QLabel(tr("switch {\t\tThe first true case condition that is encountered will\n case x > 1: a;\tdetermine the result of the switch. If none of the case\n case x < 1: b;\tconditions hold true, the default action is used\n default: c;\tto determine the return value\n}")));
212 
213  QWidget *control2_page = new QWidget();
214  QVBoxLayout *control2_layout = new QVBoxLayout(control2_page);
215  control2_layout->addWidget(new QLabel(tr("while (conditon) {\tEvaluates expression repeatedly as long as condition is true,\n expression;\treturning the last value of expression\n}")));
216  control2_layout->addWidget(new QLabel(tr("repeat\t\tEvalues expression repeatedly as long as condition is false,\n expression;\treturning the last value of expression\nuntil (condition)\n")));
217  control2_layout->addWidget(new QLabel(tr("for (var x := 0; condition; x += 1) {\tRepeatedly evaluates expression while the condition is true,\n expression\t\t\twhile evaluating the 'increment' expression on each loop\n}")));
218  control2_layout->addWidget(new QLabel(tr("break\t\tTerminates the execution of the nearest enclosed loop, returning NaN")));
219  control2_layout->addWidget(new QLabel(tr("break[x]\t\tTerminates the execution of the nearest enclosed loop, returning x")));
220  control2_layout->addWidget(new QLabel(tr("continue\t\tInterrupts loop execution and resumes with the next loop iteration")));
221  control2_layout->addWidget(new QLabel(tr("return[x]\t\tReturns immediately from within the current expression, returning x")));
222  control2_layout->addWidget(new QLabel(tr("~(expr; expr; ...)\tEvaluates each sub-expression and returns the value of the last one\n~{expr; expr; ...}")));
223 
224  QWidget *example_page = new QWidget();
225  QVBoxLayout *example_layout = new QVBoxLayout(example_page);
226  for (const pair<string, string> &entry : Examples) {
227  const string &desc = entry.first;
228  const string &example = entry.second;
229 
230  QLabel *desc_label = new QLabel("<b>" + tr(desc.c_str()) + "</b>");
231  desc_label->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::LinksAccessibleByMouse | Qt::LinksAccessibleByKeyboard);
232  desc_label->setOpenExternalLinks(true);
233  example_layout->addWidget(desc_label);
234  QPushButton *btn = new QPushButton(tr("Copy to expression"));
235  connect(btn, &QPushButton::clicked, this, [&]() { set_expr(QString::fromStdString(example)); });
236  QGridLayout *gridlayout = new QGridLayout();
237  gridlayout->addWidget(new QLabel(QString::fromStdString(example)), 0, 0, Qt::AlignLeft);
238  gridlayout->addWidget(btn, 0, 1, Qt::AlignRight);
239  example_layout->addLayout(gridlayout);
240  }
241 
242  // Create the rest of the dialog
243  QDialogButtonBox *button_box = new QDialogButtonBox(
244  QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
245 
246  QTabWidget *tabs = new QTabWidget();
247  tabs->addTab(basics_page, tr("Basics"));
248  tabs->addTab(func1_page, tr("Functions 1"));
249  tabs->addTab(func2_page, tr("Functions 2"));
250  tabs->addTab(trig_page, tr("Trigonometry"));
251  tabs->addTab(logic_page, tr("Logic"));
252  tabs->addTab(control1_page, tr("Flow Control 1"));
253  tabs->addTab(control2_page, tr("Flow Control 2"));
254  tabs->addTab(example_page, tr("Examples"));
255 
256  QVBoxLayout *root_layout = new QVBoxLayout(this);
257  root_layout->addWidget(tabs);
258  root_layout->addWidget(expr_edit_);
259  root_layout->addWidget(button_box);
260 
261  // Set tab width to 4 characters
262 #if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
263  expr_edit_->setTabStopDistance(util::text_width(QFontMetrics(font()), "XXXX"));
264 #else
265  expr_edit_->setTabStopWidth(util::text_width(QFontMetrics(font()), "XXXX"));
266 #endif
267 
268  connect(button_box, SIGNAL(accepted()), this, SLOT(accept()));
269  connect(button_box, SIGNAL(rejected()), this, SLOT(reject()));
270 }
271 
272 void MathEditDialog::set_expr(const QString &expr)
273 {
274  expr_edit_->document()->setPlainText(expr);
275 }
276 
278 {
279  math_signal_->set_expression(expr_edit_->document()->toPlainText());
280  QDialog::accept();
281 }
282 
284 {
285  math_signal_->set_expression(old_expression_);
286  QDialog::reject();
287 }
288 
289 
291  pv::Session &session,
292  shared_ptr<data::SignalBase> base) :
293  AnalogSignal(session, base),
294  math_signal_(dynamic_pointer_cast<pv::data::MathSignal>(base))
295 {
296  delayed_expr_updater_.setSingleShot(true);
298  connect(&delayed_expr_updater_, &QTimer::timeout,
299  this, [this]() { math_signal_->set_expression(expression_edit_->text()); });
300 }
301 
302 void MathSignal::populate_popup_form(QWidget *parent, QFormLayout *form)
303 {
304  AnalogSignal::populate_popup_form(parent, form);
305 
306  expression_edit_ = new QLineEdit();
307  expression_edit_->setText(math_signal_->get_expression());
308 
309  const QIcon edit_icon(QIcon::fromTheme("edit", QIcon(":/icons/math.svg")));
310  QAction *edit_action =
311  expression_edit_->addAction(edit_icon, QLineEdit::TrailingPosition);
312 
313  connect(expression_edit_, SIGNAL(textEdited(QString)),
314  this, SLOT(on_expression_changed(QString)));
315  connect(edit_action, SIGNAL(triggered(bool)),
316  this, SLOT(on_edit_clicked()));
317  form->addRow(tr("Expression"), expression_edit_);
318 
319  sample_count_cb_ = new QComboBox();
320  sample_count_cb_->setEditable(true);
321  sample_count_cb_->addItem(tr("same as session"));
322  sample_count_cb_->addItem(tr("100"));
323  sample_count_cb_->addItem(tr("10000"));
324  sample_count_cb_->addItem(tr("1000000"));
325  connect(sample_count_cb_, SIGNAL(editTextChanged(QString)),
326  this, SLOT(on_sample_count_changed(QString)));
327  form->addRow(tr("Number of Samples"), sample_count_cb_);
328 
329  sample_rate_cb_ = new QComboBox();
330  sample_rate_cb_->setEditable(true);
331  sample_rate_cb_->addItem(tr("same as session"));
332  sample_rate_cb_->addItem(tr("100"));
333  sample_rate_cb_->addItem(tr("10000"));
334  sample_rate_cb_->addItem(tr("1000000"));
335  form->addRow(tr("Sample rate"), sample_rate_cb_);
336 }
337 
338 void MathSignal::on_expression_changed(const QString &text)
339 {
340  (void)text;
341 
342  // Restart update timer
343  delayed_expr_updater_.start();
344 }
345 
346 void MathSignal::on_sample_count_changed(const QString &text)
347 {
348  (void)text;
349 }
350 
352 {
354  dlg.set_expr(expression_edit_->text());
355 
356  dlg.exec();
357 }
358 
359 } // namespace trace
360 } // namespace views
361 } // namespace pv
MathSignal(pv::Session &session, shared_ptr< data::SignalBase > base)
Definition: mathsignal.cpp:290
std::streamsize text_width(const QFontMetrics &metric, const QString &string)
Definition: util.cpp:290
static const vector< pair< string, string > > Examples
Definition: mathsignal.hpp:49
void populate_popup_form(QWidget *parent, QFormLayout *form)
Definition: mathsignal.cpp:302
void populate_popup_form(QWidget *parent, QFormLayout *form)
static std::string data()
Definition: exprtk.hpp:39024
shared_ptr< pv::data::MathSignal > math_signal_
Definition: mathsignal.hpp:63
void set_expr(const QString &expr)
Definition: mathsignal.cpp:272
#define MATHSIGNAL_INPUT_TIMEOUT
Definition: mathsignal.cpp:44
pv::Session & session_
Definition: signal.hpp:111
shared_ptr< pv::data::MathSignal > math_signal_
Definition: mathsignal.hpp:80
MathEditDialog(pv::Session &session, shared_ptr< pv::data::MathSignal > math_signal, QWidget *parent=nullptr)
Definition: mathsignal.cpp:80
libsigrok allows users to import and export data from files in various formats some of them as generic as others very specific For a list and make sure to check not so common and outright exotic ways to represent data and sigrok tries to suit as many needs as it can To see which formats your version of PulseView just click on the small arrow next to the _Open_ PulseView will ask for the file name to open Once you picked the you may be asked to specify the details of the if the input module requires them For example
void on_sample_count_changed(const QString &text)
Definition: mathsignal.cpp:346
void on_expression_changed(const QString &text)
Definition: mathsignal.cpp:338