Working with data
Computed signals & formulas
Derive a signal from others, or edit a signal in place. Both use the same expression language.
Two kinds of formula
| Computed signal | In-place edit | |
|---|---|---|
| Creates | A new trace | Nothing; modifies an existing trace |
| References | Any signal | Only the trace being edited |
| Original values | Untouched | Kept; reversible |
| Opened with | ⌥⌘N | Double-click a trace |
The same language is used a third time, without creating anything, in the calculation pad — a scratch pane for expressions over whatever the measurement tools currently hold.
Creating a computed signal
Press ⌥⌘N, or choose File → New Computed Signal…
- Library — every signal and function, searchable. Click to insert at the caret.
- Expression — syntax highlighted, with autocomplete as you type.
- Status line — the specific error, or a green check plus any alignment warnings.
- Live preview — the result drawn as you type, with statistics and the merged axis.
The Library lists every signal and function; click one to insert it. The Preview draws the result as you type. Create is enabled once the formula compiles.
Free allows 2 computed signals, Personal 5. Pro and Team are unlimited.
Editing and deleting
Double-click a computed signal to reopen its definition. Saving updates every track that draws it. Delete it with × or the context menu; the Computed source disappears when its last signal is deleted.
Editing a signal in place
Double-click any trace. The pane opens with the trace's own name as the formula, ready to build
on: voltage * 0.001, bits(status) & 0xFF,
raw - mean(raw).
- An in-place edit may reference only the trace being edited.
- The values as loaded are kept, so the edit can be changed or removed at any time.
- Everything downstream sees the edited values, including formulas that reference the trace.
- Reset to Loaded Values removes the edit.
The formula language
References
Write a signal's name. Bracket names that are not plain identifiers:
voltage
[motor current]
Qualify a name with its source when two sources share it:
[run1.csv].voltage - [run2.csv].voltage
Clicking a signal in the Library inserts the correct form.
Numbers
Decimal (1, 2.5, 1e-3) and hexadecimal
(0xFF).
Operators
| Operator | Meaning | Precedence |
|---|---|---|
^ | Power (right-associative) | 6 |
* / | Multiply, divide | 5 |
+ - | Add, subtract | 4 |
<< >> | Shift (int only) | 3 |
& | Bitwise AND (int only) | 2 |
| | Bitwise OR (int only) | 1 |
Bitwise operators bind less tightly than + and -; parenthesise when in
doubt.
Row indexing
A reference can be narrowed to one row, or to a span of rows. Both are 0-based, and a span is inclusive at both ends, so it reads the way the row numbers do on screen.
voltage[0] the first row
voltage[2:5] rows 2, 3, 4 and 5
Only a reference can be indexed — wrap an expression in a signal of its own first. A row outside the data yields no value; a span is clamped to the rows that exist.
What a row is depends on where the formula is written. In a computed signal it is a sample.
In the calculation pad it is a row of the
measurement table, so voltage[3] is that signal's value at cursor 3.
Scalars and series
Numbers and reducer results are scalars and combine elementwise with a series, so
voltage - mean(voltage) removes a DC offset.
Functions
All functions take one argument except xor. Per-sample maps each sample,
series depends on neighbouring samples, and scalar reduces to one number.
The scalar functions skip samples that have no value, so a gap in a signal cannot poison a
statistic. count reports how many samples were left.
| Function | Kind | Description |
|---|---|---|
diff(x) | series | Difference between consecutive samples. First sample is NaN. |
cumsum(x) | series | Running total. |
abs(x) | per-sample | Absolute value. |
sqrt(x) | per-sample | Square root. Negative samples become NaN. |
log(x) | per-sample | Natural logarithm. |
exp(x) | per-sample | e to the power of each sample. |
sin(x) | per-sample | Sine, in radians. |
cos(x) | per-sample | Cosine, in radians. |
mean(x) | scalar | Average of all samples. |
min(x) | scalar | Smallest sample. |
max(x) | scalar | Largest sample. |
sum(x) | scalar | Total of all samples. |
count(x) | scalar | How many samples have a value. |
median(x) | scalar | Middle sample, averaging the two middle values for an even count. |
std(x) | scalar | Population standard deviation (divides by N). |
rms(x) | scalar | Root mean square. |
bits(x) | per-sample | Reinterpret each sample's float32 bit pattern as an int. |
xor(a, b) | per-sample | Bitwise XOR of two ints. |
not(x) | per-sample | Bitwise complement of an int. |
int(x) | per-sample | Round to the nearest whole number. |
float(x) | per-sample | Relabel an int as a float. |
Types
Values are either float (a number) or int (a bit pattern).
Which one a signal is comes from the type its source declared — an int32 Parquet
column or a 16-bit WAV is an int, a CSV column is a float. The formula pane tags each signal with
its declared type. See Sample types.
Bitwise operators and bit functions accept ints only. A signal whose source declared an integer
type takes them directly — counter & 0xFF needs no cast. Applying one to a
float is an error: "& needs an int — wrap 'channel' in bits() or int() first". Numeric
literals work with either.
bits() and int()
bits(x)reinterprets the sample's float32 bits as an integer. Use it when a device packs flags into a float field.int(x)converts, rounding to the nearest whole number. Use it when a signal holds integers but arrived as a float — a CSV column, for instance, since delimited text declares no type.
Bitwise arithmetic is 32-bit signed.
A computed signal is tagged simply int or float — a formula result
carries no declared width.
Alignment
Signals in one formula are merged onto a common x-axis before evaluation. See How axes are combined. Three consequences:
- Inputs are forward-filled, not interpolated: a slow signal holds its value between updates.
- Positions before an input's first sample are NaN, which propagates through arithmetic.
- An input plotted against a non-increasing custom x-axis is sorted first. The formula pane warns when this happens.
The editor
Typing raises a suggestion list. Tab or Return accepts the highlighted entry, arrow keys cycle, Esc dismisses. With no list showing, Return commits.
The operator palette inserts + - * / ^, the bitwise operators and a parenthesis
pair. Clear empties the field.
The status line reports the first error, or a green check plus any alignment warnings. The preview shows the result type, min, max, mean and NaN count, the resolved inputs, and the merged axis.
Live updates
Computed signals re-evaluate when their inputs change, including on every window update from a live stream. A computed signal counts as live if any of its inputs is live, so Follow live and the follow-a-signal y mode work with derived curves.
Renaming and broken signals
Renaming a signal updates every formula that references it.
If an input becomes unavailable — its file is closed, or a computed signal it read is deleted — the dependent signal is marked broken, dims in the browser and stops drawing. Reopen the source and re-save the formula to restore it.
Examples
sqrt(ax^2 + ay^2 + az^2)
voltage - mean(voltage)
(signal - min(signal)) / (max(signal) - min(signal))
cumsum(current) * 0.001
[run_a.csv].temperature - [run_b.csv].temperature
bits(status) & 0xFF