Delay
Delay holds a signal back by a whole number of task cycles and passes it on unchanged.6 minute read
Delay holds a signal back by a whole number of task cycles and passes it on
unchanged. Use it to line signals up in time — a sensor reading against the
command that produced it, a feedforward term against the feedback path it has
to meet. It changes nothing about the signal’s size or shape: every frequency
comes through at full amplitude, just later.
It is the one block here whose job is to add lag rather than avoid it.
flowchart LR
i1(["input — signal to hold back"]) --> B["Delay"]
B --> o1(["output — the same signal, later"])
Delay in seconds =
delaysamples× task period [s]. On a 1 ms task,delaysamples25 is 25 ms. The lag is the same at every frequency, and it reaches half a cycle of phase at $1/(2 \times$ delay$)$ Hz — 20 Hz for a 25 ms delay. Inside a feedback loop that is where the delay alone inverts the sign, so a delay in a loop costs stability directly.
This block has no enable, no disable, no isEnabled and no reset. It is
always running. Set delaysamples to 0 to pass the signal straight through.
The delay is counted in samples, not seconds — see Limits and errors.
Signals
Inputs
| Path | Unit | Range | Description |
|---|---|---|---|
input |
signal unit | unbounded | The signal to hold back. One element per channel; the channel count is fixed by the machine configuration. |
Outputs
| Path | Unit | Description |
|---|---|---|
output |
signal unit | The same signal, delaysamples cycles later, unchanged in size and shape. Equals input exactly while delaysamples is 0. Reads zero for the first delaysamples cycles after every controller start, then begins reproducing the input. |
Parameters
| Path | Unit | Default | Range | Effect |
|---|---|---|---|---|
delaysamples |
samples | 0 | 0 – a ceiling fixed when the machine is built | How many task cycles to hold the signal back, shared by all channels. 0 passes the signal straight through. A value above the ceiling is corrected silently — see Limits and errors. Note the path name is all lower case. |
delaysamples is persistent and survives a controller restart. The input and
output do not. No parameters exist below this block.
Setup
-
Work out the delay you need in seconds, then divide by the task period to get samples. On a 1 ms task, 25 ms is 25 samples.
-
Find the ceiling: write a large number to
delaysamplesand read it back. The value you get is the largest delay this instance supports, and it is fixed when the machine is built. -
Set
delaysamplesto 0 and link the source signal intoinput.outputequalsinputsample for sample, which confirms the link. -
Set
delaysamplesto the value from step 1. Read it back and confirm it was accepted.Step 4 emits stale data for a moment. Raising the delay while the machine runs pushes out samples that were never recorded at the new depth. Change
delaysampleswith the axis at rest, or accept a brief burst of wrong values onoutput. -
Trace
inputandoutputtogether and measure the offset between the same feature on each. It should be your delay in seconds. -
Trace every element of both. An element that reads zero while the machine moves is an unwired channel.
Tuning
There is almost nothing to tune. The delay is either the right number of samples or it is not.
- Measure the misalignment you are correcting, in seconds, off a trace of the two signals you want to line up.
- Divide by the task period and round to the nearest whole number. This block cannot delay by a fraction of a sample, so the task rate sets how finely you can align.
- Set
delaysamples, then re-measure the offset on a trace. Adjust by one sample at a time. - If the alignment you need is finer than one task period, this block cannot deliver it. Raise the task rate, or accept the rounding.
- Re-check
delaysamplesafter any task-rate change. The delay is counted in samples, so the same setting is a different delay in seconds. Rescale it by the ratio of the old and new task periods. - If the delay sits inside a feedback loop, re-check the loop’s stability after every change. A delay costs phase margin at every frequency.
Read the delay off any curve as its horizontal offset from the dashed input.
| Symptom | Cause | Action |
|---|---|---|
output reads zero for a while after every start |
Expected: the block has nothing recorded yet, so it emits zeros for delaysamples cycles |
Gate the consumer off a delay of at least that long after start |
A burst of wrong values on output right after raising delaysamples |
Expected: the deeper slots hold data that was never recorded | Change the delay at rest, not in motion |
output jumped after lowering delaysamples |
Expected: the output skips forward in time by the difference | Change the delay at rest, or step it down one sample at a time |
| The delay in seconds changed after a task-rate change | Expected: the delay is counted in samples, not seconds | Rescale delaysamples by the ratio of the task periods |
delaysamples reads back lower than written |
Above the ceiling for this instance | Work within the value you read back; the ceiling is fixed when the machine is built |
| You need a delay finer than one task period | Not possible — only whole samples are available | Raise the task rate, or accept the rounding |
The loop that consumes output went unstable |
Expected: a delay costs phase margin at every frequency | Reduce the delay, or retune the loop for it |
| You cannot switch the block off | Expected: there is no enable of any kind | Set delaysamples to 0, which is a true pass-through |
| You cannot flush the stored samples | There is no reset in this block | Set delaysamples to 0 and back, or restart the controller |
output is a filtered version of input rather than a shifted one |
Not possible — this block cannot change a signal’s shape | Check you are looking at this block’s output and not a filter’s |
| Some channels are delayed more than others | Not possible — all channels share one delay | Use a separate instance per channel group |
A non-numeric value appeared on output |
A non-numeric value reached input delaysamples cycles ago |
Fix the source; the value clears itself after delaysamples cycles |
| The block costs more processing time than expected | Expected: the work grows with the delay, not with the signal | Use the smallest delay that does the job |
A starting point for aligning a sensor reading with a 25 ms transport lag on a 1 ms task:
delaysamples = 25
Limits and errors
| Limit | Set by | What happens | Reported |
|---|---|---|---|
delaysamples ≤ the instance ceiling |
Machine configuration | A larger value is replaced by the ceiling. The ceiling is not readable directly — discover it with Setup step 2 | Silently; read delaysamples back |
| Delay resolution | Task rate | Whole samples only. The task period is the finest alignment available | Not reported |
| Delay in seconds | Task rate | Scales directly with the task period, so a task-rate change silently changes the delay in wall-clock terms | Not reported |
output |
Nothing | Unbounded — whatever the input carries reaches the output unchanged. Limit it downstream if the consumer needs a bound | Not reported |
| Stored samples | Nothing | There is no reset input and no enable. Setting delaysamples to 0 and back is the only way to flush from the parameter tree |
Not reported |
| Channel count | Machine configuration | Fixed once the controller starts; it cannot be changed at runtime | Not reported |
The block raises no errors or warnings and logs nothing. Every failure above shows as a value on a trace, not as a message.
Verified against motorcortex-control3 3.30.0 (bc348fd).