Skip to content
Get started

Sampled controls

Standard library

Eqiora.Controls.Sampled supplies a unit delay and a discrete integrator. Each occurrence uses the caller’s clock and owns one initialized memory.

Component Input Tick outputs
UnitDelay u: 1 y is the previous memory; current u becomes the next memory
DiscreteIntegrator rate: 1 / s before is old memory; after is old memory plus period times rate
sampled.eqi
import Eqiora.Controls.Sampled.sampled as sampled;
model SampledSignals(
input signal: 1 at tick,
input rate: 1 / s at tick,
output delayed: 1 at tick,
output accumulated: 1 at tick
) {
clock tick = periodic(1[s] / 4, phase = 0[s] / 1);
instance delay: sampled.UnitDelay(tick = tick, initial_value = 5);
instance integral: sampled.DiscreteIntegrator(tick = tick, initial_value = 3);
connect signal -> delay.u;
connect rate -> integral.rate;
relation outputs at tick {
delayed = delay.y;
accumulated = integral.after;
}
}

The first tick is at zero and the period is 1/4 s. With input samples [2, -1, 3], delayed is [5, 2, -1]. With rate samples [2, -1, 3] /s, accumulated is [3.5, 3.25, 4]. Select integral.before instead of integral.after to observe memory before the current increment.

Place this source in src/main.eqi of a local project and add Eqiora.Controls.Sampled at version 0.1.0 using the package workflow. Compile with entry="SampledSignals". The complete sampled-controls example shows how to supply timestamped values and run an execution_session.

sampled.eqi — library definitions
/// Publish the previous sample and then retain the current input.
public component UnitDelay(
clock tick: periodic,
parameter initial_value: 1,
input u: 1 at tick,
output y: 1 at tick
) {
state memory: 1 at tick;
initial { pre(memory) = initial_value; }
relation update at tick {
y = pre(memory);
next(memory) = u;
}
}
/// Integrate the supplied rate once per tick, exposing both sides of the update.
public component DiscreteIntegrator(
clock tick: periodic,
parameter initial_value: 1,
input rate: 1 / s at tick,
output before: 1 at tick,
output after: 1 at tick
) {
state memory: 1 at tick;
initial { pre(memory) = initial_value; }
relation update at tick {
next(memory) = pre(memory) + period(tick) * rate;
before = pre(memory);
after = next(memory);
}
}

initial_value and tick are required bindings. The integrator uses the supplied period, so changing the period changes its increment. No output sample exists before the first tick. All equations at a tick hold simultaneously.

These blocks use dimensionless memory. To integrate a physical rate, choose a fixed nonzero scale in the desired output unit. Divide the physical rate by the scale, use a dimensionless initial value, and multiply the output by the same scale. For example, with a 2 V scale, 4 V/s becomes 2 /s; a quarter-second step increases normalized memory by 0.5 and physical voltage by 1 V.

The example project contains voltage and displacement adapters. They are ordinary equations and introduce no additional state.

Inputs must use the same clock as their receiving blocks. Two separate clocks with equal periods remain different clocks; sharing tick makes the intended activation explicit.

Clock syntax · State and storage