24 #include <QDialogButtonBox> 25 #include <QFormLayout> 26 #include <QGridLayout> 29 #include <QPushButton> 32 #include <QVBoxLayout> 44 #define MATHSIGNAL_INPUT_TIMEOUT (2000) 47 {
"Product of signals named A1 and A2:",
49 {
"Create a sine wave with f=1MHz:",
50 "sin(1e6 * 2 * pi * t)"},
51 {
"Average the past 4 samples of A2:",
54 "for (var i := 0; i < n; i += 1) {\n" \
55 "\tv += sample('A2', s - i) / n;\n" \
57 {
"Create a <a href=https://en.wikipedia.org/wiki/Chirp#Linear>frequency sweep</a> from 1Hz to 1MHz over 10 seconds:",
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" \
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)"}
81 shared_ptr<pv::data::MathSignal> math_signal, QWidget *parent) :
84 math_signal_(math_signal),
85 old_expression_(math_signal->get_expression()),
86 expr_edit_(new QPlainTextEdit())
88 setWindowTitle(tr(
"Math Expression Editor"));
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"));
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")));
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")));
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")));
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"));
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}")));
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; ...}")));
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;
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);
243 QDialogButtonBox *button_box =
new QDialogButtonBox(
244 QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
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"));
256 QVBoxLayout *root_layout =
new QVBoxLayout(
this);
257 root_layout->addWidget(tabs);
259 root_layout->addWidget(button_box);
262 #if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) 268 connect(button_box, SIGNAL(accepted()),
this, SLOT(
accept()));
269 connect(button_box, SIGNAL(rejected()),
this, SLOT(
reject()));
292 shared_ptr<data::SignalBase> base) :
309 const QIcon edit_icon(QIcon::fromTheme(
"edit", QIcon(
":/icons/math.svg")));
310 QAction *edit_action =
315 connect(edit_action, SIGNAL(triggered(
bool)),
QTimer delayed_expr_updater_
MathSignal(pv::Session &session, shared_ptr< data::SignalBase > base)
std::streamsize text_width(const QFontMetrics &metric, const QString &string)
QComboBox * sample_count_cb_
QPlainTextEdit * expr_edit_
static const vector< pair< string, string > > Examples
void populate_popup_form(QWidget *parent, QFormLayout *form)
void populate_popup_form(QWidget *parent, QFormLayout *form)
shared_ptr< pv::data::MathSignal > math_signal_
void set_expr(const QString &expr)
#define MATHSIGNAL_INPUT_TIMEOUT
QComboBox * sample_rate_cb_
shared_ptr< pv::data::MathSignal > math_signal_
MathEditDialog(pv::Session &session, shared_ptr< pv::data::MathSignal > math_signal, QWidget *parent=nullptr)
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
QLineEdit * expression_edit_
void on_sample_count_changed(const QString &text)
void on_expression_changed(const QString &text)