SimpleSwitch3D

SimpleSwitch3D cross-fades between two sets of signals and their first and second derivatives.
3.30–3.34

SimpleSwitch3D cross-fades between two sets of signals and their first and second derivatives. A boolean picks which set you want and all three levels travel there together over a configured time.

Use it where a feedforward path downstream needs position, velocity and acceleration together, and the source of all three has to change.

The 3D in the name means three derivative levels, not three axes. The channel count is set when the controller is built and defaults to one, the same as SimpleSwitch.

flowchart LR
    i1(["input1, input1Dot, input1DDot"]) --> B["SimpleSwitch3D"]
    i2(["input2, input2Dot, input2DDot"]) --> B
    i3(["toggle"]) --> B
    i4(["doInstantToggle"]) --> B
    B --> o1(["output, outputDot, outputDDot"])
    B --> o2(["isOn"])
    B --> o3(["isOff"])
    B --> o4(["faderValue"])

During a fade, outputDot is not the rate of change of output. All three levels are blended with the same fraction, which leaves out the part of the derivative that comes from the blend itself moving. The error is zero before and after the fade, largest in the middle, and grows with the gap between the two sources. The figure under Tuning shows the size of it.

Do not feed outputDot to anything that must agree with output during a handover. Differentiate output yourself, or keep the two sources close together while you switch.

Fading in and fading out are configured separately — their own times and their own shapes.

Signals

Inputs

Path Unit Range Description
input1 signal unit unbounded The value selected when toggle is false. This is where the block starts.
input1Dot signal unit per second unbounded Its rate of change. You supply this — the block does not compute it.
input1DDot signal unit per second² unbounded Its acceleration. Also supplied by you.
input2 signal unit unbounded The value selected when toggle is true.
input2Dot signal unit per second unbounded Its rate of change.
input2DDot signal unit per second² unbounded Its acceleration.
toggle - true or false Which input set the output should travel to. A level, not a pulse. Flipping it back mid-fade reverses from wherever it had got to.
doInstantToggle - true or false While true, all three outputs snap to the selected set every cycle with no fade. Also a level.

Nothing checks that the derivative inputs really are the derivatives of the value inputs. If you wire them inconsistently you get a consistent blend of inconsistent data.

Outputs

Path Unit Description
output signal unit The blended value.
outputDot signal unit per second The blended rate. Not the rate of change of output during a fade — see the callout above.
outputDDot signal unit per second² The blended acceleration. Same caveat, larger.
isOn - The fade to input2 has completed. Goes true a few cycles early.
isOff - The fade to input1 has completed. Starts true.
faderValue - How far through the fade, 0 to 1. The straight-line fraction even when the shape is cosine.

Parameters

Path Unit Default Range Effect
fadeInTime s 5.0 one task period to 10 How long it takes to travel to input2. Values above 10 are silently reduced to 10, with a warning in the log.
fadeOutTime s 5.0 one task period to 10 How long it takes to travel back. Same ceiling.
fadeInType - 0 0 or 1 Shape of the fade to input2. 0 straight line, 1 cosine.
fadeOutType - 0 0 or 1 Shape of the fade back to input1.
timeScaleFactor - 1.0 0 upward Speeds up or slows down the fade while it runs. 0 freezes it. Reset to 1 every time the controller starts.

All are persistent and survive a controller restart, except that timeScaleFactor is forced back to 1 at startup. No parameters exist below this block.

fadeInType / fadeOutType What the machine does Effect on the derivative error
0 straight line Constant blend rate, stopping dead at each end The error is constant through the fade and steps at both ends
1 cosine The blend eases away from one set and into the other The error is zero at both ends and peaks in the middle. Larger at its peak than the straight line, but smooth

Setup

  1. Connect both sets of three signals. All six must have the same number of channels.

  2. Leave toggle false. Confirm output matches input1 and isOff is true.

  3. Set fadeInTime and fadeOutTime. Start with a second or two.

  4. Set both shapes to 1 unless you need the shortest transit.

  5. Bring the two sources close together before switching — this both reduces the motion and reduces the derivative error to nearly nothing.

  6. Set toggle true and watch all three outputs travel to the input2 set.

    Step 6 hands the machine over to whatever input2 is carrying, and the derivative outputs will be wrong for the duration of the fade. If anything downstream acts on outputDot, check the size of the gap first.

  7. Confirm isOn goes true and the outputs match the input2 set.

Tuning

  1. Trace both value inputs together before switching. The gap between them drives both the motion and the derivative error.
  2. Close that gap before switching wherever you can. A handover between two sources that already agree is nearly free.
  3. Set the fade time from the gap and what the machine can take.
  4. Lengthen the fade to reduce the derivative error: the error is proportional to the gap divided by the fade time, so doubling the time halves it.
  5. Use the cosine shape for a smooth blend, the straight line if you would rather the error be constant and predictable than smooth.
  6. Watch faderValue for the fade progress. With the cosine shape the outputs lag it early and lead it late.
  7. Fade times are in seconds, so a task-rate change does not move them.

During a cosine fade between two sources held still, the published outputDotstays at zero while the output is visibly moving. The true rate of change peaksin the middle of the fade; the published value never leaveszero.

With both sources standing still the whole of outputDot is missing. In general the shortfall is the gap between the sources times the blend’s own rate.

Symptom Cause Action
A feedforward path fought the machine during a handover outputDot did not match output during the fade Differentiate output yourself, or close the gap before switching
outputDot read zero while output was moving Expected: both sources were still, so the whole derivative is the blend’s own motion, which is not published See the callout at the top
The derivative error is worse than I expected The gap is large or the fade is short Close the gap, or lengthen the fade
outputDDot is further out than outputDot Expected: the second derivative is missing two terms, not one Same actions
The machine moved when I flipped the toggle Expected: the two value inputs held different values Match them first, or lengthen the fade
The outputs stepped instead of fading doInstantToggle is true Set it false — it is a level, not a pulse
The fade is shorter than I asked for A time above 10 is reduced to 10 Check the log; 10 seconds is the maximum
The fade time reads back as a tiny number A value below one task period was raised to it Write a longer time
The fade is slower or faster than the time I set timeScaleFactor is not 1 Set it to 1
timeScaleFactor reverted after a restart Expected: it is forced to 1 at startup Set it from your application after start
The fade froze part-way timeScaleFactor is 0 Set it above 0
The switch stuck at one end timeScaleFactor is negative, which is not rejected Write a positive value
The switch never fades at all The task period read as zero at startup, which is not guarded here Restart the controller
A rate step at the start or end The straight-line shape does that Set the type to 1
isOn went true slightly early Expected: the completion test has a small tolerance Watch faderValue
A signal scaled by faderValue does not match Expected: it is the straight-line fraction Apply the same cosine yourself
The derivative outputs never made sense The derivative inputs may not be the derivatives of the value inputs Nothing checks this — verify upstream
Everything became invalid and stayed that way A fade time that is not a number is not caught Rewrite the fade times

A starting point for a handover between two profile sources:

fadeInTime      = 2.0
fadeOutTime     = 0.5
fadeInType      = 1
fadeOutType     = 1
timeScaleFactor = 1.0

Limits and errors

Limit Set by What happens Reported
Derivative consistency Nothing outputDot and outputDDot do not match output during a fade. The shortfall is zero at both ends and peaks in the middle, proportional to the gap between the sources Not reported at all
Derivative inputs Nothing Not checked against the value inputs. Inconsistent inputs blend consistently Not reported
Fade duration fadeInTime, fadeOutTime All three levels reach the far end in exactly that time, divided by timeScaleFactor faderValue, isOn, isOff
Fade time range Fixed at one task period to 10 s Out-of-range values are clamped and written back Logged as a warning, once per write
Output rate during a fade Nothing Not limited. Use SoftSwitch when the output’s derivatives must stay within limits Not reported
timeScaleFactor Nothing Not checked. 0 freezes; negative runs the fade backwards until it sticks Not reported
timeScaleFactor persistence Fixed Forced to 1 at every controller start Not reported
Task period Not guarded here A zero task period leaves the fade frozen. SimpleSwitch guards this; this block does not Not reported
faderValue Fixed Always the unshaped fraction Not applicable
doInstantToggle The input A level. While true, every cycle snaps faderValue reads 0 or 1
Values that are not numbers Nothing Not caught by the range check Not reported
Startup state Fixed All three outputs start on the input1 set; isOff starts true isOff
Channel count Fixed at build time All six inputs and three outputs share one channel count Not reported

The block logs a warning when a fade time is written outside its range. Every other condition above shows as a value on a trace, or not at all.


Verified against motorcortex-control3 3.30.0 (bc348fd).