Formula

The Formula math module calculates a new channel from an expression you write. The expression can use any channel in the setup (analog inputs, CAN, GPS, counters, other math channels) along with numbers, operators and built-in functions. Channels with different sample rates can be mixed in one formula, and one formula can use the output of another.

Adding a formula

Go to Config → Math and click Formula in the toolbar. If it isn’t there, open Add math and pick Formula from the Formula and scripting group.

To add several formulas at once, open Add math, right-click Formula and choose Add multiple, then enter how many you want.

The Formula setup window

Formula setup window

The left side defines the output channel:

  • Name, Description, Units and Color of the new channel.
  • Preview shows the minimum, maximum, average and RMS of the result, calculated from live data while you type.
  • Values sets the display range and number format.
  • Time axis sets the timebase of the output (see Timebase).

The right side is the editor:

  • The preview at the top shows the formula as a typeset equation.
  • Below it is the text field where you type the formula.
  • Basic operators and Other math functions are buttons that insert an operator or function at the cursor. Hover over a button to see its syntax and a short description.
  • The channel list shows every channel you can use. Double-click a channel to insert it. Use the search box and filter button to narrow down a long list.

The preview makes long formulas easier to check, because fractions, roots, powers and absolute values are drawn the way they would be on paper:

Formula preview

Errors

When the formula can’t be parsed, the preview turns red, an arrow marks where the problem is, and the error is shown at the bottom of the window. The most common errors are:

Error Usual cause
Channel not found The name between the quotes doesn’t match a channel in the setup.
Invalid function name A function name is misspelled, or the function doesn’t exist.
Syntax error A missing bracket, a missing operator, or a stray character.
array mismatch Two arrays of different length are combined.

Channel not found

Invalid function name

Syntax error

Array mismatch

Click OK to add the channel. The formula is then listed in the Math grid.

Editing a formula in the grid

The Setup configuration column of the Math grid shows the formula text, and you can edit it there without opening the setup window. You can also edit it in the channel grid. This makes it easy to copy formulas between setups or paste them from a spreadsheet.

Formula in the Math grid

Timebase

The Time axis tab sets the timebase of the output channel:

  • Auto picks the timebase from the inputs. A formula on a synchronous channel is synchronous with the same sample rate. A formula on asynchronous channels is asynchronous. With several inputs, the output follows the fastest one. A formula with no channels at all (for example 2 * pi) becomes a single value.
  • Sync forces a synchronous output. Choose the sample rate divider from the SRDiv list. Asynchronous inputs are interpolated to this rate before the formula is calculated, which gives a smoother result.
  • Async makes the output follow the timing of one asynchronous channel, selected in the Channel list. Only asynchronous channels used in the formula appear there.
  • Single value gives one value with no time stamp. Use it for results that summarize the whole measurement.

When a formula combines channels with different sample rates, the values of the slower channel are held until the next value arrives.

Writing formulas

  • Put channel names in single quotes: 'AI 1'. Names are not case-sensitive, so 'ai 1' also works.
  • Function names and operators are not case-sensitive either: SIN, Sin and sin are the same.
  • Use a dot as the decimal separator (0.5), regardless of your Windows regional settings. Scientific notation works too (1.5e-3).
  • Spaces and line breaks are ignored, so long formulas can be split over several lines.
  • Add parentheses whenever the order of operations isn’t obvious.

Operators and functions

Basic operators

Operator Syntax Result
+ - * / a + b Addition, subtraction, multiplication, division
^ a ^ b a raised to the power b
( ) (a + b) * c Grouping
div a div b Integer part of a / b
mod a mod b Remainder of a / b

mod is calculated as a - b * trunc(a / b), so the result has the sign of a:

Expression Result
420 div 720 0
740 div 720 1
420 mod 720 420
740 mod 720 20
-8 mod 3 -2
8 mod -3 2

Functions

Functions tab

Function Result
sqr(x) x²
sqrt(x) Square root of x
abs(x) Absolute value
sgn(x) 1 if x > 0, 0 if x = 0, -1 if x < 0
trunc(x) x with the decimals cut off: trunc(-1452.457) = -1452
round(x) x rounded to the nearest whole number
rnd A random number between 0 and 1, new for every sample
log2(x), log10(x), ln(x) Logarithm with base 2, base 10 and base e
exp(x) eˣ
if(cond, a, b) a when cond is not zero, otherwise b
nan No value. Only useful in asynchronous output (see Caveats)
max(a, b), min(a, b) The larger or smaller of two values

To round to a step other than 1, scale before and after rounding: round('x' / 10) * 10 rounds to tens, round('x' * 10) / 10 rounds to one decimal.

max and min take two arguments. Nest them for more: max(max('p1', 'p2'), 'p3').

Trigonometry

Trigonometry tab

Function Result
sin(x), cos(x), tan(x) Sine, cosine, tangent. x is in radians
cotan(x) Cotangent (not on the button panel)
asin(x), acos(x), atan(x) Inverse functions, result in radians
atan(y, x) Angle of the point (x, y), from -π to π
pi π

To convert degrees to radians, multiply by pi / 180.

Logic

Logic tab

Operator Result
< > <= >= = <> 1 if the comparison is true, otherwise 0
not a 1 if a is 0, otherwise 0
a and b 1 if both are non-zero
a or b 1 if at least one is non-zero
a xor b 1 if exactly one is non-zero
a eqv b 1 if both are zero or both are non-zero (not on the button panel)
a imp b Logical implication: 0 only when a is non-zero and b is zero (not on the button panel)
if(cond, a, b) Same as on the Functions tab

Because comparisons return 0 or 1, you can use them as multipliers. ('sine' > 0) * 'sine' keeps the positive half of a signal and sets the rest to 0.

Signals

Signals tab

Function Result
sr Sample rate of the formula channel
scnt Number of samples since acquisition started; restarts when storing starts
time Seconds since acquisition started; restarts when storing starts
acqtime Seconds since acquisition started; does not restart when storing starts
abstime Absolute UTC time, in days since 30 December 1899
localabstime Absolute local time, in days since 30 December 1899
sine(f [, ps]) Sine wave of frequency f [Hz] with optional phase shift ps [rad]
square(f [, ps]) Square wave
trian(f [, ps]) Triangle wave
noise Random numbers between -1 and 1
gaussnoise Normally distributed random numbers (not on the button panel)
omega 2π · elapsed time, a ready-made phase for your own waveforms (not on the button panel)

The resolution of time follows the sample rate of the channel: at 100 Hz it counts in steps of 0.01 s.

Generated signals work in math, displays and storage. They can’t drive an analog output. Use the function generator in Analog out for that.

Example, angle from an encoder clocked externally at 720 pulses per revolution: (scnt mod 720) * 0.5 gives the angle in degrees. A missed or extra pulse shifts the angle permanently, so if the encoder also has a zero (trigger) pulse, use a counter in angle mode instead.

Measure

Measure tab

The measure functions react to edges, the moment a condition changes from 0 to 1. Write the condition as a comparison, like 'TTL' > 2.5. Passing a raw channel such as ecnt('TTL') treats every non-zero value as 1, which is rarely what you want for an analog signal.

Function Result
edge(cond [, rearm]) 1 on the sample where cond changes from 0 to 1, otherwise 0. With rearm, a new edge is only accepted after rearm has been true
ecnt(cond) Number of edges of cond
icnt(cond) Number of samples in which cond was 1
pulsewidth(cond [, rearm]) Time in seconds between two consecutive edges of cond
stopwatch(cond1, cond2) Time in seconds from an edge of cond1 to an edge of cond2
measdiff(value, cond1, cond2) Change of value between an edge of cond1 and an edge of cond2
hold(value, cond [, rearm]) Holds value from the moment of an edge of cond until the next accepted edge
trig 1 when a storing trigger occurs
trigstatus Trigger state: 0 not armed, 1 armed, 2 triggered

The rearm condition filters out noise around the threshold. With hold('pressure', 'temp' > 30, 'temp' < 28), the pressure is held when the temperature rises above 30. If the temperature then dips to 29.8 and goes back over 30, nothing happens. A new value is only held after the temperature has first dropped below 28.

The resolution of the time-based functions is one sample, so a higher sample rate gives a more precise result. To measure the frequency of an analog sine wave, the Exact frequency math is a better choice than pulsewidth.

Example, braking time and braking distance from 60 km/h down to 2 km/h:

stopwatch('velocity' < 60, 'velocity' < 2)
measdiff('distance', 'velocity' < 60, 'velocity' < 2)

Events

Events tab

Function Result
keypressed(code) 1 for one sample when the key with this virtual key code is pressed
storing 1 while data is being stored, otherwise 0

The buttons for digits, arrows and space insert keypressed with the right code. For other keys, use the Windows virtual key code in decimal: 48 to 57 for the digits 0 to 9, 65 to 90 for the letters A to Z, 32 for space, 37/38/39/40 for left/up/right/down, and 13 for Enter.

Example, zeroing a channel at the press of a key: 'pressure' - hold('pressure', keypressed(49) > 0.5) subtracts the value the pressure had when 1 was last pressed.

Example, a counter that goes up with the right arrow and down with the left: ecnt(keypressed(39) > 0.5) - ecnt(keypressed(37) > 0.5).

Complex

Complex tab

Function Result
complex(re, im) Complex number re + im·i
real(z), imag(z) Real and imaginary part
abs(z) Magnitude, √(re² + im²)
angle(z) Phase angle in radians
conj(z) Complex conjugate, re - im·i

Arrays

Arrays tab

Array channels come from FFT, CPB, scope, classification, counting and other modules. You can also build arrays in the formula.

Syntax Result
'ch'[i] Element at index i. The first element is 0
'ch'{p} Element at axis position p, for example the FFT line at 50 Hz: 'AI 1/AmplFFT'{50}
'ch'[n:m] Elements from index n to m. len - 1 stands for the last element: 'ch'[10:len-1]
'ch'{p1:p2} Elements between two axis positions
[a, b, c] Array of the listed values
[a:b] Array of the values from a to b
ones(n), zeroes(n) Array of n ones or n zeros
min(arr), max(arr) Smallest and largest element
minind(arr), maxind(arr) Index of the smallest and largest element
minpos(arr), maxpos(arr) Axis position of the smallest and largest element, for example the frequency of the highest FFT peak
sum(arr), avg(arr) Sum and average of all elements
integrate(arr) Running integral of the array

Arithmetic works element by element. 'AI 0/AmplFFT' - 'AI 1/AmplFFT' subtracts the two spectra line by line, and 'AI 0/AmplFFT' + 2 adds 2 to every line. Both arrays must have the same length.

History

History tab

Syntax Result
'ch'.data(n) One value of the channel at position n. n can be positive or negative
prev The previous result of this formula. prev + 1 counts samples, prev + 'ch' / sr integrates a channel

Channel properties

Channel properties tab

Syntax Result
'ch'.data(n) Same as on the History tab
'ch'.len Number of elements of an array channel
'ch'.axis(n) Axis values of an array or matrix channel. 0 is the first axis, 1 the second

Caveats

An invalid operation sets the whole result to 0

Division by zero, the square root of a negative number and the logarithm of a negative number don’t produce an error or NaN. The whole formula outputs 0 for that sample, not just the part that failed. sqrt('AI 1') + 5 outputs 0, not 5, for every sample where 'AI 1' is negative:

Minimum is 0, not 5

A 0 in the result can therefore mean “invalid input” as well as a real zero. If that matters, keep the input in range, for example sqrt(abs('x')) or ln(max('x', 1e-9)).

if calculates both branches

if evaluates both results before choosing one, so it can’t protect a division from a zero denominator. The formula below outputs 0 for every sample, not 5, because 1 / 0 is calculated even though that branch is never used:

if('b' <> 0, 1 / 'b', 5)

if returns 0 instead of 5

Guard the denominator itself instead:

'a' / if('b' <> 0, 'b', 1) * ('b' <> 0)

nan only works in asynchronous output

nan means “no value here” and leaves a gap in an asynchronous channel. In a synchronous channel every sample must have a value, so nan becomes 0. if('AI 1' > 0, 'AI 1', nan) with the default timebase shows 0 wherever the input is negative. To get gaps, set the timebase to Async, choose an asynchronous channel used in the formula, and turn interpolation off.

Logic treats any non-zero value as true

not, and, or, xor and the condition of if and the measure functions treat any non-zero value as 1. 'AI 1' and 1 is always 1, even when the signal is negative. Compare analog signals to a threshold first, for example ('speed' > 100) and ('rpm' > 2000).

Comparing analog values with = is also unreliable, since a measured value is almost never exactly equal to a number. Use a range instead: abs('x' - 5) < 0.01.

Angles are in radians

All trigonometric functions use radians. sin(90) is the sine of 90 radians, not of 90 degrees. Write sin(90 * pi / 180).

atan with two arguments

The tooltip describes atan(x, y) as the arctangent of x/y. In fact, atan(a, b) returns the angle of the point with coordinates (b, a): the first argument is the vertical component and the second the horizontal one, and the result covers the full circle from -π to π. atan(1, -1) is 2.356 (135°), whereas atan(1 / -1) is -0.785 (-45°).

zeroes, not zeros

The tooltip on the zeroes button says ZEROS(COUNT), but the function is called zeroes. Typing zeros gives Invalid function name.

Counters restart when storing starts

scnt and time restart from 0 when storing starts, so values calculated during the preview differ from those in the data file. Use acqtime if you need a time that doesn’t restart.

Use a fixed number for the key code

Always pass a plain number to keypressed, like keypressed(49). Don’t calculate the key code from a channel or another expression.

Generated signals don’t reach outputs

sine, square, trian, noise and the other generators exist only inside Dewesoft. They can be displayed, stored and used in other math, but can’t be sent to an analog output.