From a31cb621d8ba917f8c73c6f52e0b13647e0a64e6 Mon Sep 17 00:00:00 2001 From: esaruoho Date: Mon, 2 Mar 2026 14:59:47 +0200 Subject: [PATCH] Require sustained coil current before relay switches (#67) Brief back-EMF transients from one relay coil could incorrectly trigger another relay because the model switched instantly when current crossed the threshold. Now the coil current must remain above/below the on/off threshold for at least half the switching time before the relay starts moving, matching real relay inertia. Co-Authored-By: Claude Opus 4.6 --- .../circuitjs1/client/RelayElm.java | 34 ++++++++++++++----- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/src/com/lushprojects/circuitjs1/client/RelayElm.java b/src/com/lushprojects/circuitjs1/client/RelayElm.java index c90cd9ccd..b848d3b25 100644 --- a/src/com/lushprojects/circuitjs1/client/RelayElm.java +++ b/src/com/lushprojects/circuitjs1/client/RelayElm.java @@ -64,6 +64,9 @@ class RelayElm extends CircuitElm { int poleCount; int openhs, dflip; boolean onState; + + // track time current has been above/below threshold to filter transients + double onThresholdTime, offThresholdTime; final int nSwitch0 = 0; final int nSwitch1 = 1; final int nSwitch2 = 2; @@ -328,6 +331,7 @@ void reset() { for (i = 0; i != poleCount; i++) switchCurrent[i] = switchCurCount[i] = 0; d_position = i_position = 0; + onThresholdTime = offThresholdTime = 0; // preserve onState because if we don't, Relay Flip-Flop gets left in a weird state on reset. // onState = false; @@ -361,14 +365,23 @@ void startIteration() { } ind.startIteration(volts[nCoil1]-volts[nCoil3]); double absCurrent = Math.abs(coilCurrent); - + + // require current to be sustained above/below threshold for a minimum + // time before switching, to filter brief transients like back-EMF spikes + double minHoldTime = switchingTime * 0.5; + if (onState) { // on or turning on. check if we need to turn off if (absCurrent < offCurrent) { - // turning off, set switch to intermediate position - onState = false; - i_position = 2; + offThresholdTime += sim.timeStep; + onThresholdTime = 0; + if (offThresholdTime >= minHoldTime) { + // turning off, set switch to intermediate position + onState = false; + i_position = 2; + } } else { + offThresholdTime = 0; d_position += sim.timeStep/switchingTime; if (d_position >= 1) d_position = i_position = 1; @@ -376,15 +389,20 @@ void startIteration() { } else { // off or turning off. check if we need to turn on if (absCurrent > onCurrent) { - // turning on, set switch to intermediate position - onState = true; - i_position = 2; + onThresholdTime += sim.timeStep; + offThresholdTime = 0; + if (onThresholdTime >= minHoldTime) { + // turning on, set switch to intermediate position + onState = true; + i_position = 2; + } } else { + onThresholdTime = 0; d_position -= sim.timeStep/switchingTime; if (d_position <= 0) d_position = i_position = 0; } - + } }