LevelSwitch
LevelSwitch compares one signal against one level and publishes two complementary flags.5 minute read
LevelSwitch compares one signal against one level and publishes two
complementary flags. A deadband holds the decision while the signal sits near
the level, so the output does not chatter on noise.
Use it to raise a flag when a pressure, a speed or a position crosses a threshold.
flowchart LR
i1(["input"]) --> B["LevelSwitch"]
p1(["switchLevel"]) --> B
p2(["deadband"]) --> B
B --> o1(["isLower"])
B --> o2(["isHigher"])
isHigheris always the exact opposite ofisLower. Read whichever reads better in your logic; they can never disagree.
The deadband is split either side of the level. A deadband of 2 around a level of 5 switches to high at 6 and back to low at 4. Size it from the noise on your signal, not from the level.
Signals
Inputs
| Path | Unit | Range | Description |
|---|---|---|---|
input |
signal unit | unbounded | The signal to compare. Compared as-is, with no filtering — feed it a filtered signal if it is noisy beyond what the deadband can absorb. |
Outputs
| Path | Unit | Description |
|---|---|---|
isLower |
- | True while the input is below the level. Starts true, before the first comparison has run. |
isHigher |
- | True while the input is above the level. Always the complement of isLower. |
Parameters
| Path | Unit | Default | Range | Effect |
|---|---|---|---|---|
switchLevel |
signal unit | 0.0 | unbounded | The level to compare against. The output switches high at switchLevel + deadband/2 and back low at switchLevel - deadband/2. |
deadband |
signal unit | 0.0 | 0 upward | Total width of the hold region, split either side of the level. With 0 the output switches exactly at the level and will chatter on any noise. A negative value removes the hysteresis and is not rejected. |
Both are persistent and survive a controller restart. No parameters exist below this block.
Setup
- Connect the signal you want to watch to
input. - Set
switchLevelto the value you want to detect. - Leave
deadbandat 0 for a first look, and watchisHigherwhile the signal crosses the level. - If
isHigherflickers at the crossing, the signal is noisy — go to Tuning. - Confirm
isLowerandisHigherare always opposite. They are computed to be, so a disagreement means you are reading two different cycles.
This block only reads a signal and raises a flag. It does not move anything and cannot be made to.
Tuning
- Trace
inputwhile the machine does what you want to detect, and note how far the signal wanders when it is sitting still near the level. - Set
deadbandto about twice that wander. That is the whole tuning. - Check the crossing again.
isHighershould switch once, cleanly, in each direction. - If it still chatters, the deadband is smaller than the noise. Double it.
- If the flag now arrives too late, the deadband is too wide — remember it delays the switch by half its width in each direction.
- If you cannot get both, the signal needs filtering upstream rather than a wider deadband here. A wider deadband costs you accuracy in both directions; a filter costs you a little delay in one.
- Nothing here depends on the task rate, so none of this needs re-checking after a rate change.
The gap between the two switching points is the deadband.
| Symptom | Cause | Action |
|---|---|---|
| The flag chatters at the crossing | deadband is smaller than the noise on the signal |
Widen it, or filter the input |
The flag chatters with deadband set |
The signal is noisier than you measured | Trace the input at rest near the level and re-measure |
| The flag arrives later than expected | Expected: the deadband delays the switch by half its width | Narrow it, or move switchLevel down by half the deadband |
| The flag never switches | The signal never reaches switchLevel ± deadband/2 |
Check the level, and check the input is actually connected |
| The hysteresis disappeared | deadband is negative, which is not rejected |
Write a positive value |
isHigher reads true at exactly the level |
Expected with a zero deadband: the tie goes to high | Set a deadband |
isLower read true before anything ran |
Expected: it starts true until the first cycle | Ignore the outputs until the controller is running |
| Both flags read the same | Not possible in one cycle | You are comparing readings from different cycles |
| The flag did not update after a restart | Expected: the previous decision is held for one cycle | Ignore the first cycle |
| A NaN reached the input and the flag froze | The decision is held when the input is not a number | Fix the upstream signal |
| I need to watch several signals | Not possible — this block is single channel | Use one instance per signal |
| I need to disable it | Not possible — there is no enable | Ignore the outputs in your own logic |
A starting point for a signal that sits within ±0.5 of its value at rest:
switchLevel = <the value you want to detect>
deadband = 2.0
Limits and errors
| Limit | Set by | What happens | Reported |
|---|---|---|---|
| Switching point | switchLevel, deadband |
High at switchLevel + deadband/2, low at switchLevel - deadband/2 |
isLower, isHigher |
| Inside the deadband | deadband |
The decision is held — this is the hysteresis and the reason the block exists | Not reported |
| Exactly at the level, zero deadband | Fixed | Reads isHigher |
Not reported |
deadband sign |
Nothing | Not checked. A negative value overlaps the two thresholds and removes the hysteresis | Not reported |
input value |
Nothing | Not checked. A NaN input holds the previous decision rather than switching | Not reported |
| Initial state | Fixed | isLower reads true and isHigher false until the first cycle runs |
Not reported |
| Channel count | Fixed | One signal per instance, always | Not reported |
| Task rate | Not used | The block has no time dependence at all; the decision is instantaneous | Not applicable |
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).