Calculated Channels
Process live telemetry with calculated channels.
Calculated channels derive real-time data from existing channels. They enable live data processing and analysis, such as:
- Scaling, converting or filtering raw data
- Implementing sensor voting algorithms
- Creating channels to conditionally trigger alarms or warnings.
Calculated channels now use the Arc language. Existing Lua-based channels will continue to work but should be migrated to Arc.
Creating Calculated Channels
To create a calculated channel, open the Search and Command Palette and run the “Create Calculated Channel” command.
- Click the search bar at the top and type
>to open the palette. - Alternatively, press
Ctrl+Shift+P(Windows) /Cmd+Shift+P(macOS).
Channel Parameters
When creating a calculated channel, you will be prompted to fill in the following fields:
| Field | Description |
|---|---|
| Name | A name for the channel. |
| Expression | The Arc expression that calculates the value to be written to the calculated channel. This expression must end with a return statement. Channel dependencies are automatically detected from the expression. |
| Operation (Optional) | Post-processing operation applied to the expression result. Options include min, max, and avg for running aggregations. |
| Window (Optional) | Time duration for operation resets. When the duration expires, the operation state is reset. Set to 0 for no duration-based reset. Only applies when an operation is selected. |
| Reset Channel (Optional) | A uint8 channel that triggers operation reset when its value equals 1. Only applies when an operation is selected. |
Writing Expressions
Calculated channels use the Arc language for expressions. Arc is a domain-specific language designed for real-time telemetry processing.
Basic Syntax
Channel References
Reference channels directly by name without any prefix or special syntax:
return temperature
return sensor_a + sensor_b
return voltage * current Channel names are automatically detected and used as inputs to the calculation.
Return Statement
Every expression must end with a return statement:
return pressure * 1.5 Variables
Use := to declare intermediate variables:
scaled := pressure * 2.5
offset := scaled + 10
return offset Operators
Arc supports standard operators for calculations:
Arithmetic: +, -, *, /, %
return (temp_1 + temp_2 + temp_3) / 3 // Average Comparison: ==, !=, <, >, <=, >=
return pressure > 100 // Returns 1 (true) or 0 (false) Logical: and, or, not
return temp > 100 and pressure > 50 Conditionals
Use if statements with curly braces for conditional logic:
if (temperature > 100) {
return 1
} else {
return 0
} Multi-condition example:
if (temp > 100 and pressure > 50) {
return 2
} else if (temp > 50) {
return 1
} else {
return 0
} Data Types
The calculated channel’s data type is automatically inferred from the Arc expression’s
return value. The system analyzes your expression to determine the appropriate data type
(e.g., float64, int32, etc.).
Editing Calculations
To edit a calculated channel, right-click it in the Channels Toolbar and select “Edit Calculation” from the context menu:
Operations
Operations are optional post-processing steps that apply aggregations to your calculated channel. They enable stateful computations like running minimums, maximums, and averages.
How Operations Work
Operations process the expression result before writing to the output channel:
Expression Result → Operation → Output Channel
↑
Reset Channel The operation maintains internal state across executions, outputting a single aggregated value rather than processing individual samples.
Supported Operations
| Operation | Description |
|---|---|
none | No operation (default). Passes expression result directly to output. |
min | Running minimum. Tracks the smallest value seen since the last reset. |
max | Running maximum. Tracks the largest value seen since the last reset. |
avg | Running average. Computes the mean of all values since the last reset. |
Reset Mechanisms
Operations can reset their state through three mechanisms (any can trigger a reset):
- Duration-based (Window): Resets after a specified time duration based on input timestamps
- Signal-based (Reset Channel): Resets when a
uint8channel receives value1 - Never: Set duration to 0 and omit reset channel for a continuous operation
Example Configurations
Running average over 10 seconds:
- Operation:
avg - Window:
10seconds - Reset Channel: (none)
The average resets every 10 seconds based on data timestamps.
Maximum until manual reset:
- Operation:
max - Window:
0(no duration reset) - Reset Channel:
manual_reset_button
The maximum runs indefinitely until the reset channel triggers.
Minimum with both reset types:
- Operation:
min - Window:
60seconds - Reset Channel:
cycle_complete
The minimum resets either after 60 seconds OR when the reset channel triggers—whichever comes first.
Reset Channels
Reset channels provide signal-based control over operation state and must have data type
uint8. When a reset channel receives a value of 1, the associated operation clears
its accumulated state and restarts.
Use Cases
Manual Reset Button:
Create a virtual uint8 channel that you can write to from a schematic button or
control panel to manually reset the calculation.
Periodic Reset Signal:
Use a timer or sequence to generate periodic reset pulses for regular operation resets.
Conditional Reset Logic:
Create a calculated channel that outputs 1 when a certain condition is met (e.g.,
“cycle complete” flag from another system).
Behavior Example
Time: 0s → 5s → 10s → 10.1s (reset=1) → 15s → 20s
Pressure: 50 → 100 → 75 → 75 → 90 → 110
Max Operation: 50 → 100 → 100 → (reset to 75) → 90 → 110 At 10.1s, the reset channel triggers, clearing the max value. The operation restarts from the current input (75) and continues tracking the new maximum.
Examples
Simple Operations
Scaling a sensor:
return pressure * 1.5 Adding an offset:
return temperature + 273.15 // Convert Celsius to Kelvin Power calculation:
return voltage * current Multi-Channel Calculations
Sum of multiple sensors:
return sensor_1 + sensor_2 + sensor_3 Average (in expression):
return (temp_1 + temp_2 + temp_3) / 3 Differential measurement:
return inlet_pressure - outlet_pressure Conditional Logic
Threshold detection:
if (pressure > 100) {
return 1 // High alarm
} else {
return 0 // Normal
} Multi-level thresholds:
if (temp > 100 and pressure > 50) {
return 2 // Critical
} else if (temp > 50) {
return 1 // Warning
} else {
return 0 // Normal
} Safe division (avoid divide-by-zero):
if (denominator == 0) {
return 0
} else {
return numerator / denominator
} Multi-Step Calculations
Unit conversion with intermediate variables:
celsius := sensor_temp
fahrenheit := celsius * 9 / 5 + 32
return fahrenheit Pressure compensation:
raw_pressure := sensor_reading
temp_correction := temperature * 0.01
compensated := raw_pressure - temp_correction
return compensated Using Operations
Running maximum with manual reset:
- Expression:
return pressure - Operation:
max - Window:
0(no duration reset) - Reset Channel:
reset_button
This tracks the maximum pressure value until the reset button is triggered.
10-second rolling average:
- Expression:
return temperature - Operation:
avg - Window:
10seconds - Reset Channel: (none)
This computes the average temperature over 10-second windows.
How Calculated Channels Work
Calculated channels are virtual, meaning their data is computed on-demand rather than stored permanently to disk. This section explains how they work under the hood.
On-Demand Computation
Calculated channels don’t run continuously in the background. Instead, they only execute when someone actively reads or streams the channel. When you start visualizing a calculated channel in a line plot or request its data through the API, Synnax automatically activates the calculation. When you stop streaming, the calculation pauses.
This on-demand approach conserves system resources: calculations only consume CPU and memory when their results are actually needed.
Historical Evaluation
Calculated channels can be evaluated historically, allowing you to query past data as if the calculation had been running at that time. For this to work:
- All input channels must have data available in the time range you’re querying
- Simple expressions (like
return pressure * 1.5) work reliably with historical data - Operations (min, max, avg) with time-based windows may have limitations with historical queries
Example: If your calculation depends on sensor_1 and sensor_2, you can query
historical data for any time range where both sensors have recorded data. The
calculation will be applied to the historical data as if it had been running
continuously.
Nested calculations also work historically. If calc_2 depends on calc_1, and
calc_1 depends on persisted channels, you can query calc_2 historically as long as
the underlying data exists.
Nested Calculations
Calculated channels can depend on other calculated channels, creating calculation chains:
sensor_raw → temp_celsius → temp_fahrenheit → temp_status When data arrives at sensor_raw, it automatically propagates through the chain:
temp_celsiuscalculates and outputs its resulttemp_fahrenheitreceives that output and calculates its resulttemp_statusreceives that output and produces the final result
There’s no limit to nesting depth, but keep in mind that each level adds computation time. Design your calculations thoughtfully to balance clarity and performance.
Handling Different Arrival Rates
Calculated channels operate on series (arrays of samples), not individual scalar
values. When you write return temperature + pressure, you’re performing an elementwise
operation on arrays.
When inputs have different numbers of samples, Arc uses last-value-hold semantics:
- The output length equals the maximum of the input lengths
- Shorter series repeat their last value for remaining positions
- This happens per frame, not across time
Example: Consider a frame where:
return temperature + pressure If the frame contains:
temperature: 10 samples[20, 21, 22, 23, 24, 25, 26, 27, 28, 29]pressure: 5 samples[30, 31, 32, 33, 34]
The calculation produces 10 output samples:
pressure’s last value (34) repeats for positions 6-10- Elementwise addition:
[50, 52, 54, 56, 58, 59, 60, 61, 62, 63]
What this means for different rates:
If temperature updates at 100 Hz and pressure at 10 Hz, when a frame arrives with
100 temperature samples and 10 pressure samples, the calculated channel produces
100 output samples. pressure’s 10th value is used for output samples 11-100.
Note: This is not interpolation; it’s a last-value-hold. The system doesn’t create
new pressure values; it reuses the most recent one.
Migration from Lua
If you have existing Lua-based calculated channels, here’s how to migrate them to Arc:
Syntax Comparison
| Feature | Lua (Legacy) | Arc (Current) |
|---|---|---|
| Channel Access | channels.name or get("name") | name |
| Variables | local x = 5 | x := 5 |
| Conditionals | if x then ... end | if (x) { ... } |
| Logical AND | and | and |
| Logical OR | or | or |
| Logical NOT | not | not |
| Not Equal | ~= | != |
| Return | return value | return value |
Migration Examples
Simple scaling:
-- Lua
return channels.sensor * 2 // Arc
return sensor * 2 Conditional logic:
-- Lua
if channels.temp > 100 then
return 1
else
return 0
end // Arc
if (temp > 100) {
return 1
} else {
return 0
} Multi-step with variables:
-- Lua
local scaled = channels.pressure * 2.5
local offset = scaled + 10
return offset // Arc
scaled := pressure * 2.5
offset := scaled + 10
return offset Key Differences
- No channel prefix needed - Reference channels by name directly
- Curly braces for blocks - Use
{}instead ofthen...end - Keyword operators - Arc uses the same
and,or,notkeywords as Lua - Auto-detected dependencies - No need to manually specify
requiresfield
Channel Naming for Arc
Important: Arc identifiers can only contain letters, digits, and underscores, and must start with a letter or underscore. Channel names with special characters (like dashes, spaces, or dots) cannot be directly referenced in Arc expressions.
Best Practice: When creating channels that will be used in Arc calculated channels, use underscores instead of special characters:
- ✅
sensor_1,temp_reading,channel_a - ❌
sensor-1,temp reading,channel.a
For complex calculations involving loops or tables, consider breaking them into multiple calculated channels.