FA-11961 / Discrete control systems / Open access
A command limiter ignores elapsed sample duration · case 01
A command limiter ignores elapsed sample duration.
ROOT CAUSE
The rate is treated as a fixed per-call increment.
VERIFIED REPAIR
Multiply rate by elapsed time and clamp signed command movement.
Unsuccessful approach: Scaling only upward movement leaves downward changes dependent on call rate.
Case contract
With nonnegative integer rate and dt, move from previous toward target by at most rate*dt, without overshoot.
Why this case matters
An offline discrete controller model isolates this state or arithmetic fault; it makes no physical plant or safety claim.
1 / The failure
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(previous, target, rate, dt):
return previous + max(-rate, min(rate, target-previous))
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
check('rise duration', solve(0, 10*N, N, 3), 3*N)
check('fall duration', solve(10*N, 0, N, 3), 7*N)
check('zero elapsed', solve(N, 0, N, 0), N)
check('no overshoot', solve(0, N, N, 3), N)
check('zero rate', solve(N, 10*N, 0, 3), N)
check('hold', solve(N, N, N, 3), N)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
| Boundary fixture | Actual | Expected | Outcome |
|---|---|---|---|
| rise duration | 1 | 3 | Failed |
| fall duration | 9 | 7 | Failed |
| zero elapsed | 0 | 1 | Failed |
| no overshoot | 1 | 1 | Passed |
| zero rate | 1 | 1 | Passed |
| hold | 1 | 1 | Passed |
SHA-256 / 7ec0408802f2517549f21172e7b8bc48e00c8bae0cef4554ac14aa07adae2b56
2 / The unsuccessful fix
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(previous, target, rate, dt):
limit = rate*dt if target >= previous else rate
return previous + max(-limit, min(limit, target-previous))
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
check('rise duration', solve(0, 10*N, N, 3), 3*N)
check('fall duration', solve(10*N, 0, N, 3), 7*N)
check('zero elapsed', solve(N, 0, N, 0), N)
check('no overshoot', solve(0, N, N, 3), N)
check('zero rate', solve(N, 10*N, 0, 3), N)
check('hold', solve(N, N, N, 3), N)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
| Boundary fixture | Actual | Expected | Outcome |
|---|---|---|---|
| rise duration | 3 | 3 | Passed |
| fall duration | 9 | 7 | Failed |
| zero elapsed | 0 | 1 | Failed |
| no overshoot | 1 | 1 | Passed |
| zero rate | 1 | 1 | Passed |
| hold | 1 | 1 | Passed |
SHA-256 / 6a495b8bbec4d282e8ded5b72f294b0be2454b548f202920ceeecea9bce857b5
3 / The verified repair
Exit 0"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(previous, target, rate, dt):
limit=rate*dt
return previous + max(-limit, min(limit, target-previous))
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
check('rise duration', solve(0, 10*N, N, 3), 3*N)
check('fall duration', solve(10*N, 0, N, 3), 7*N)
check('zero elapsed', solve(N, 0, N, 0), N)
check('no overshoot', solve(0, N, N, 3), N)
check('zero rate', solve(N, 10*N, 0, 3), N)
check('hold', solve(N, N, N, 3), N)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
| Boundary fixture | Actual | Expected | Outcome |
|---|---|---|---|
| rise duration | 3 | 3 | Passed |
| fall duration | 7 | 7 | Passed |
| zero elapsed | 1 | 1 | Passed |
| no overshoot | 1 | 1 | Passed |
| zero rate | 1 | 1 | Passed |
| hold | 1 | 1 | Passed |
SHA-256 / 33f3aca7bb108dbe4e30b6969b5a866c3975d88d6544a4889b5eccc8971c1101
Verification & scope
Integer-valued controlled examples only; no physical plant, stability guarantee, timing jitter, or hardware behavior is modeled. This reproducer isolates one failure mechanism. Results cover the supplied fixtures. Variants within a family share a test contract and should remain grouped when constructing evaluation splits. Related mechanisms with a shared evaluation_group must also remain together; these controlled models are not independent production incidents.
Observations recorded using Python 3.12.14 at 2026-09-29T14:38:52.476871+00:00.
Case digest / c2b9d96b0a48218d37e859d5efa1c77348c2afb9f0aa6c4ed0df82105fb45c11