Fader
Fader moves its output smoothly to whatever value its input holds, over a configured fade time, and publishes the matching velocity and acceleration as it goes.8 minute read
Fader moves its output smoothly to whatever value its input holds, over a
configured fade time, and publishes the matching velocity and acceleration as
it goes.
Write a new number to input and the output travels there. Write another
before it arrives and the fade blends into the new target rather than
restarting — the output, its velocity and its acceleration all stay continuous.
flowchart LR
i1(["input"]) --> B["Fader"]
i2(["timeScaleFactor"]) --> B
i3(["timeScaleFactorDot"]) --> B
B --> o1(["output"])
B --> o2(["outputDot"])
B --> o3(["outputDDot"])
B --> o4(["done"])
B --> o5(["delaying"])
B --> o6(["numTargets"])
The fade time is what you set. Unlike
SetpointGenerator, this block does not work out how fast it may go — it takes exactlyfadeTimeseconds whatever the distance, so a large jump means a fast move. SizefadeTimefrom the largest jump you expect.
Targets queue up. Each change to
inputadds one. When the queue is full the oldest is dropped, and the only sign is thatnumTargetsstops growing. Watch it if your application writes targets quickly.
Four of the parameters below may not exist on your instance.
fadeTime,delayTime,jerkFactorandaccelerationFactorare hidden when the block that contains this fader controls them itself. If you cannot find them, they are being set for you.
Signals
Inputs
| Path | Unit | Range | Description |
|---|---|---|---|
input |
signal unit | unbounded | The target. A change queues a new fade; writing the same value again does nothing. Compared exactly, so an input that dithers in its last digit will queue a target every cycle and keep the queue full. Feed it settled values. |
timeScaleFactor |
- | any | Stretches the fade’s clock. 1 is normal, 0.5 is half speed, 0 freezes the fade exactly where it is and nothing is lost — it resumes when you set it back. A negative value is used as its magnitude; the fade does not run backwards. |
timeScaleFactorDot |
1/s | any | How fast timeScaleFactor is itself changing. Set this whenever you ramp the scale factor, or the published acceleration will be wrong. |
Outputs
| Path | Unit | Description |
|---|---|---|
output |
signal unit | The value on its way to the target. |
outputDot |
signal unit per second | Its velocity — this really is the rate of change of output, including across a retarget. |
outputDDot |
signal unit per second² | Its acceleration. |
done |
- | The queue is empty and the output has arrived. Starts true. |
delaying |
- | The newest target is still waiting out its delay and has not started moving. |
numTargets |
- | How many targets are queued. If this stops growing while you keep writing, the oldest are being dropped. |
Parameters
| Path | Unit | Default | Range | Effect |
|---|---|---|---|---|
faderType |
- | 0 | 0 or 1 | Which profile shape. See the table below. Changing it while a fade is running switches that fade’s shape part-way, which produces a kink — change it only when done is true. |
maxTargets |
- | 10 | 1 upward | How many targets may be queued. Values below 1 are treated as 1. When the queue is full the oldest target is dropped and its position becomes the new starting point. |
applyTimeScaleToDelay |
- | true | - | Whether timeScaleFactor stretches delayTime as well as the fade itself. |
fadeTime |
s | 1.0 | above 0 | How long the fade takes, whatever the distance. Negative values are used as their magnitude and written back. A fade shorter than one task period snaps instead. |
delayTime |
s | 0.0 | 0 upward | How long to wait after a new target before moving. Negatives are used as their magnitude. |
jerkFactor |
- | 0.333 | 0 to 0.5 | Only used by the jerk-limited shape. The fraction of the profile spent changing acceleration. Values outside the range are clamped and written back. |
accelerationFactor |
- | 0.429 | 0 to 0.5 | Only used by the jerk-limited shape. The fraction spent accelerating. Also clamped and written back. |
All are persistent and survive a controller restart. No parameters exist below this block.
faderType |
What the machine does | Parameters that matter |
|---|---|---|
| 0 quintic | Position, velocity and acceleration all start and end at zero. The smoothest shape, and the default | fadeTime |
| 1 jerk-limited | Reaches its acceleration and velocity plateaus and holds them, so more of the move happens at full speed | fadeTime, jerkFactor, accelerationFactor |
Setup
-
Have your application seed the fader at the machine’s current value before enabling anything downstream, so the first fade does not start from zero.
-
Set
fadeTimefrom the largest jump you expect and what the machine can take at that distance. -
Leave
faderTypeat 0 for a first pass. -
Write a small change to
inputand watchoutputtravel there.Step 4 moves the machine, over
fadeTimeseconds regardless of how far it has to go. Start with a small change. -
Confirm
donegoes false at the start and true on arrival. -
Write a second target while the first is still moving.
outputshould bend towards the new value with no step inoutputDot. -
Check
numTargetsgrows and falls back to 0.
Tuning
fadeTimeis the only number that matters for most uses. Set it from the biggest jump: the peak velocity is roughly twice the distance divided by the fade time for the quintic shape.- If the fade is smooth but too slow for small jumps and too fast for large
ones, this block is the wrong one —
SetpointGeneratorderives its duration from velocity and acceleration limits instead. - Switch to the jerk-limited shape if you need more of the move spent at full speed. It reaches its plateaus and holds them, so it covers ground faster for the same duration.
- With the jerk-limited shape, tune
jerkFactorandaccelerationFactortogether. Both are fractions of the profile and both cap at 0.5. The pairs 0.333 with 0.333, and 0.333 with 0.429, are the two standard settings. - Use
delayTimewhen several faders should start together after a common trigger. Decide whether the delay should stretch with the time scale and setapplyTimeScaleToDelayto match. - Use
timeScaleFactorfor a speed override. Ramp it rather than stepping it, and settimeScaleFactorDotwhile you do, oroutputDDotwill be wrong for as long as the ramp lasts. - Raise
maxTargetsif your application writes targets faster than the fades complete, and watchnumTargetsto see whether the queue is filling.
The velocity trace has no step where the second target arrives. That continuity is what this block is for.
| Symptom | Cause | Action |
|---|---|---|
| A large jump moved much faster than a small one | Expected: every fade takes fadeTime regardless of distance |
Use SetpointGenerator if you need a velocity limit |
| The move was instant | fadeTime plus delayTime is under one task period |
Lengthen fadeTime |
| A kink appeared mid-fade | faderType was changed while a fade was running |
Change it only when done is true |
numTargets stopped growing while I kept writing |
The queue is full and the oldest targets are being dropped | Raise maxTargets, or write less often |
| The output jumped to an unexpected place | An old target was dropped from a full queue, moving the starting point | Same |
| The output never settles | input is dithering, so a new target is queued every cycle |
Feed it settled values |
| Everything became invalid and stayed that way | An input that is not a number queues a target every cycle | Fix the upstream signal |
| The fade froze part-way | timeScaleFactor is 0 |
Set it above 0. Nothing was lost — it resumes |
I set a negative timeScaleFactor and it went forwards anyway |
Expected: the magnitude is used | This block cannot run backwards |
outputDDot was wrong while I changed the speed override |
timeScaleFactorDot was left at 0 |
Set it while ramping timeScaleFactor |
outputDot disagrees with output |
It should not — this block carries derivatives correctly | Check what is consuming them |
| The delay did not scale with the override | applyTimeScaleToDelay is false |
Set it true |
fadeTime reads back positive after I wrote a negative |
Expected: the magnitude is used | Write positive values |
jerkFactor reads back as 0.5 |
Expected: it is capped at 0.5 | Use a value in range |
| The first fade went to the wrong place | The fader was not seeded at the machine’s value | Seed it from your application before enabling |
| A fade resumed after a restart | The queue is not cleared at startup | Seed the fader after every start |
I cannot find fadeTime in the tree |
The block containing this fader controls it | Set it there instead |
| I need this on several channels | Not possible — this block is single channel | Use one instance per channel |
A starting point:
fadeTime = 1.0
delayTime = 0.0
faderType = 0
maxTargets = 10
applyTimeScaleToDelay = true
Limits and errors
| Limit | Set by | What happens | Reported |
|---|---|---|---|
| Fade duration | fadeTime |
Exactly that long, whatever the distance. Not derived from any velocity or acceleration limit | done |
| Output velocity | Nothing | Not limited. It is whatever the distance and fade time imply | outputDot |
| Sub-cycle fades | Checked | A fadeTime plus delayTime under one scaled task period snaps to the target |
done stays true |
fadeTime, delayTime signs |
Checked | Negatives are used as their magnitude and written back | Read the values back |
jerkFactor, accelerationFactor |
Checked | Clamped to 0 to 0.5 and written back | Read the values back |
maxTargets |
Checked | Forced to at least 1. When the queue is full the oldest target is dropped and its position becomes the new start | numTargets |
timeScaleFactor |
Partly checked | Negatives are negated. 0 freezes the fade with nothing lost | Not reported |
timeScaleFactorDot |
Nothing | Not checked. Leaving it at 0 while ramping the scale makes outputDDot wrong |
Not reported |
faderType mid-fade |
Nothing | Not guarded. An in-flight fade switches shape and keeps the old shape’s timing, producing a kink | Not reported |
| Values that are not numbers | Nothing | Not checked. Such an input queues a new target every cycle | numTargets sits at its maximum |
| Startup | Fixed | The queue is not cleared, so a fade in flight resumes after a restart | done, numTargets |
| Channel count | Fixed | One channel per instance, always | Not reported |
This block logs nothing, ever. Every condition above shows as a value on a trace, or not at all.
Verified against motorcortex-control3 3.30.0 (bc348fd).