RALGOL language reference
2026-06-05
Introduction
This is the reference for the RALGOL onboard scripting language: the syntax, and the function libraries available to a script. It complements the scripting guide, which is the tutorial; this document is the lookup table.
A note on scope: the device validates every script when you deploy it, checking that each function and field you use is actually available on that device. The functions listed here are the ones the standard libraries provide, but a specific device may ship a different or extended set. Treat this list as representative, and let the device’s deploy-time check be the authority.
Syntax
Script structure
A script has an interface block declaring its own
fields and a script block that runs once per cycle. An
optional requires {} block lists external variables (it is
otherwise inferred).
1w interface {
1w emit unsigned ticks;
} ral;
script {
std::add(ral.ticks, 1) -> ral.ticks;
};
Interface block
Sizes are given in words (1w = 32 bits) or bits
(12b). A field is at most 32 bits and may not cross a word
boundary; the whole interface is a whole number of words. An
execution-engine interface is named ral.
Field types:
| Type | Meaning |
|---|---|
unsigned / signed |
Integer, with an optional {unit = ...} and
{valid = ...}. |
bool |
One-bit true/false. |
enum { k = v, ... } |
Named integer states. |
oneof |
Tagged union — one region decoded differently per mode. |
reserved |
Padding; not exposed. |
A field may also be an array,
e.g. 4b unsigned channels[1..8].
Units and valid ranges:
{unit = 0.001 A}— the integer is interpreted in these physical units.{valid = (0, [1:2000])}— allowed values are 0, or 1 through 2000. The set is a parenthesised list of single values and[low:high](or[low:step:high]) ranges.
Field flags:
| Flag | Meaning |
|---|---|
emit |
Stream the field continuously over LSL. |
protected |
Readable by clients, not writable from outside the script. |
hidden |
Not shown in discovery (still usable if the name is known). |
persistent |
Value survives across script reloads (shareable between scripts). |
const |
Fixed configuration value, set once. |
Script block
Statements run top to bottom each cycle and end with
;.
- Assignment
expr -> target;stores the value on the left into the variable on the right. - Structured binding
[a, b] -> [x, y];assigns several values at once. - Function calls use
namespace::name(args)and nest:std::add(1, std::add(2, 3)). The operators+ - * /are shorthand forstd::add,std::subtract,std::multiply,std::divide. - Literals are integers
(
true/falsestand for 1/0). There are no floating-point literals; fractional quantities are handled by field units and the x1000 scaling convention (below). - Comments are
//to end of line, or/* ... */.
Variables
A variable is module.index.field; the index counts
instances from zero (ads.0.voltage_chan_1). For your own
interface, ral.field is short for ral.0.field.
Array elements and slices use [i], [a..b],
[..].
Control flow
There are no loops (the script is the loop). Three if
forms, each closed by fi;:
- Truish — runs when the expression is non-zero:
if (expr) : ... fi; - Equality —
if (expr == N) : ... fi; - Switch-like — integer cases with
is, optionalelse(which must be last):
1w interface {
1w reserved;
} ral;
script {
if (dio.0.digout_1)
is 0: 1 -> dio.0.digout_1;
is 1: 0 -> dio.0.digout_1;
fi;
};
Stateful instances
Primitives that persist across cycles are declared once in a
prolog {} block (which must be first in
script {}) and bound to an @-name; methods use
@name::method():
1w interface {
1w emit signed average_x1000;
} ral;
script {
prolog {
let ringbuffer(64) -> @window;
};
@window::append(ads.0.voltage_chan_1);
@window::mova() -> ral.average_x1000;
};
Library conventions
A few conventions apply across the function libraries:
- Integer ABI. All arguments and return values are 64-bit integers. There is no floating-point type in a script.
- x1000 scaling. Functions whose natural result is fractional return it scaled by 1000 (three decimal places). Angles are in milli-radians or milli-degrees, frequencies in millihertz, and so on; the description notes the scaling where it applies.
- Variadic functions flatten their arguments. Where a
signature shows
values..., you may pass several scalars and/or array slices; they are flattened into one sequence. For examplestd::mean(ral.buffer[1..8])averages the eight elements. - Element-wise vs. reducing. Some variadic functions
reduce to a single value (
sum,mean); others return one value per input (abs,sqrt,sort). - Integer math. Mathematical functions operate on
integers and truncate, e.g.
std::sqrt(10)is 3.
In the tables below, a signature like name(a, b) -> 1
means two arguments and one return value;
name(values...) -> 1 is variadic reducing to one;
name(values...) -> n returns as many values as it
received.
std — standard library
Comparison
Each returns 1 (true) or 0 (false).
| Signature | Description |
|---|---|
std::gt(a, b) |
a greater than b |
std::lt(a, b) |
a less than b |
std::ge(a, b) |
a greater than or equal |
std::le(a, b) |
a less than or equal |
std::eq(a, b) |
a equal to b |
std::ne(a, b) |
a not equal to b |
Logic
| Signature | Description |
|---|---|
std::and(a, b) |
logical AND (1/0) |
std::or(a, b) |
logical OR |
std::not(a) |
logical NOT |
std::xor(a, b) |
bitwise/logical XOR |
std::nand(a, b) |
NOT AND |
std::nor(a, b) |
NOT OR |
Arithmetic
| Signature | Description |
|---|---|
std::add(a, b) |
a + b (also the + operator) |
std::subtract(a, b) |
a - b (also -) |
std::multiply(a, b) |
a x b (also *) |
std::divide(a, b) |
a / b; errors on division by zero (also /) |
std::modulo(a, b) |
remainder a mod b; returns 0 if b is 0 |
std::power(a, b) |
a raised to the power b |
std::abs(values...) -> n |
absolute value, element-wise |
std::sqrt(values...) -> n |
integer square root, element-wise |
std::sign(values...) -> n |
-1, 0, or 1 per element |
std::exp(values...) -> n |
e^x, element-wise (integer) |
std::log(values...) -> n |
natural log, element-wise (integer) |
std::log10(values...) -> n |
base-10 log, element-wise (integer) |
std::gcd(a, b) |
greatest common divisor |
std::lcm(a, b) |
least common multiple |
std::factorial(n) |
n! (memoised) |
std::clamp(x, lo, hi) |
x limited to the range [lo, hi] |
std::is_even(values...) -> n |
1 if even, else 0, per element |
std::is_odd(values...) -> n |
1 if odd, else 0, per element |
std::is_prime(n) |
1 if n is prime, else 0 |
Statistics (reduce a sequence to one value)
| Signature | Description |
|---|---|
std::sum(values...) |
sum of all elements |
std::prod(values...) |
product of all elements |
std::min(values...) |
minimum |
std::max(values...) |
maximum |
std::range(values...) |
max - min |
std::mean(values...) |
arithmetic mean |
std::median(values...) |
median |
std::variance(values...) |
population variance |
std::std_dev(values...) |
population standard deviation |
std::sum_of_squares(values...) |
sum of each element squared |
std::rms(values...) |
root mean square |
std::skewness(values...) |
skewness |
std::kurtosis(values...) |
excess kurtosis (- 3) |
std::harmonic_mean(values...) |
harmonic mean |
std::geometric_mean(values...) |
geometric mean |
std::iqr(values...) |
interquartile range (Q3 - Q1) |
Vector operations (return a sequence)
| Signature | Description |
|---|---|
std::sort(values...) -> n |
ascending sort |
std::reverse(values...) -> n |
reverse order |
std::mode(values...) -> n |
most frequent value(s) |
std::zscore(values...) -> n |
standardised score per element |
std::assign(a, b) |
assign (exposed form of the built-in assignment) |
Data generation
| Signature | Description |
|---|---|
std::random(size, max) |
sequence of size random integers in [0, max] |
std::iota(start, end) |
sequence start, start+1, …, end |
mntg — montage helpers
| Signature | Description |
|---|---|
mntg::bipolarize(a, b) |
bipolar derivation a - b |
mntg::laplacian(c, n1, n2, n3, n4) |
surface Laplacian: centre - mean of 4 neighbours |
rgu — phase utilities
For phase-locked stimulation. Angles in milli-radians (mRad) or milli-degrees (mdeg), frequencies in millihertz (mHz), all x1000.
| Signature | Description |
|---|---|
rgu::phase_wrap_deg(phi_mRad) |
wrap a phase into [0, 360000) and convert mRad -> mdeg |
rgu::delta_phase_deg(freq_mHz, latency_samples, rate_hz) |
phase-advance correction for the system’s per-sample latency, in mdeg |
rgu::phase_in_window(phi_mDeg, lo_mDeg, hi_mDeg) |
1 if the phase is inside the window [lo, hi) (handles wrap-around), else 0 |
rgu::sine_wave(t_ms, freq_mHz, phase_mRad) |
sine value: sin(2*pi*t*freq + phase) x 1000 + 1000 |
pst —
phase-specific stimulation
Specialised real-time helpers. EEGSyncTMS is an
experimental, hardware-specific state machine; its argument units are
still being finalised.
| Signature | Description |
|---|---|
pst::sine_wave(t_ms, freq_mHz, phase_mRad) |
sine value (same as rgu::sine_wave) |
pst::EEGSyncTMS(ch0, ch1, trigger, trigger_number, trigger_frequency, trigger_phase) |
EEG phase-synchronised TMS trigger; returns 1 when it fires |
ringbuffer —
rolling-window analysis
Declared with let ringbuffer(N) -> @name; in the
prolog and used through the method-call form
@name::method(...). Analysis results are scaled x1000.
| Method | Description |
|---|---|
@buf::append(sample) |
push one sample into the window |
@buf::reset() |
empty the window, keep the storage |
@buf::clear() |
release the storage |
@buf::mova() |
moving average over the window (x1000) |
@buf::sd() |
sample standard deviation, Bessel-corrected (x1000) |
@buf::fft(bin) |
magnitude of a single DFT bin (x1000) |
@buf::fft_phase(bin) |
phase of a single DFT bin, in milli-radians (x1000) |
io — diagnostics
| Signature | Description |
|---|---|
io::print(x) |
write a value to the device log (for debugging) |
harvard — study helpers
| Signature | Description |
|---|---|
harvard::trialduration(bsl_cycles, stim_cycles, dead_cycles, m_off_freq, s_off_freq, i_freq) |
estimated trial duration for a stimulation protocol |
References
- The scripting guide (tutorial) and the device-specific function list are available from neuroConn.