JMRI® is...
Signaling
Adding signals to your layout with JMRI.
Tools
JMRI tools for working with your layout:
Layout Automation
Use JMRI to automate parts of your layout and operations:
Supported Hardware
JMRI supports a wide range of DCC systems, command stations and protocols.
Applications
By the community of JMRI.org:

JMRI Help:

Contents Index
Glossary FAQ

Donate to JMRI Donate to JMRI.org

JMRI: Simple Signal Logic

Background information of the logic used to control simple signals

This page describes the logic used by the JMRI Simple Signal panel to control signals.

We display the actual code, so there's no ambiguity about what it's doing. This is from JMRI test release 2.9.1.

On Single Block

This signal protects one end of a straight through block, with no signaled turnouts.

    void doSingleBlock() {
        int appearance = SignalHead.GREEN;
        int oldAppearance = ((SignalHead)outputs[0]).getAppearance();
        // check for yellow, flashing yellow overriding green
        if (protectWithFlashing && fastestColor1()==SignalHead.YELLOW)
            appearance = SignalHead.FLASHYELLOW;
        if (fastestColor1()==SignalHead.RED || fastestColor1()==SignalHead.FLASHRED)
            appearance = SignalHead.YELLOW;

        // if distant signal, show exactly what the home signal does
        if (distantSignal)
            appearance = fastestColor1();

        // if limited speed and green, reduce to yellow
        if (limitSpeed1)
            appearance = slowerOf(appearance, SignalHead.YELLOW);

        // check for red overriding yellow or green
        if (watchSensor1!=null && watchSensor1.getBean().getKnownState() != Sensor.INACTIVE)
            appearance = SignalHead.RED;
        if (watchSensor2!=null && watchSensor2.getBean().getKnownState() != Sensor.INACTIVE)
            appearance = SignalHead.RED;
        if (watchSensor3!=null && watchSensor3.getBean().getKnownState() != Sensor.INACTIVE)
            appearance = SignalHead.RED;
        if (watchSensor4!=null && watchSensor4.getBean().getKnownState() != Sensor.INACTIVE)
            appearance = SignalHead.RED;

        // check if signal if held, forcing a red aspect by this calculation
        if (((SignalHead)outputs[0]).getHeld())
            appearance = SignalHead.RED;

        // handle approach lighting
        doApproach();

        // show result if changed
        if (appearance != oldAppearance) {
            ((SignalHead)outputs[0]).setAppearance(appearance);
            if (log.isDebugEnabled()) log.debug("Change appearance of "+name+" to "+appearance);
        }
    }

On Main Leg ofTrailing-Point Turnout

This signal is along the main route through a turnout, which is defined as the direction taken by trains when the turnout is closed. It's protecting the frog of the turnout so that trains will stop before running through a turnout set against them.

    void doTrailingMain() {
        int appearance = SignalHead.GREEN;
        int oldAppearance = ((SignalHead)outputs[0]).getAppearance();
        // check for yellow, flashing yellow overriding green
        if (protectWithFlashing && fastestColor1()==SignalHead.YELLOW)
            appearance = SignalHead.FLASHYELLOW;
        if (fastestColor1()==SignalHead.RED || fastestColor1()==SignalHead.FLASHRED)
            appearance = SignalHead.YELLOW;

        // if distant signal, show exactly what the home signal does
        if (distantSignal)
            appearance = fastestColor1();

        // if limited speed and green, reduce to yellow
        if (limitSpeed1)
            appearance = slowerOf(appearance, SignalHead.YELLOW);

        // check for red overriding yellow or green
        if (watchSensor1!=null && watchSensor1.getBean().getKnownState() != Sensor.INACTIVE)
            appearance = SignalHead.RED;
        if (watchSensor2!=null && watchSensor2.getBean().getKnownState() != Sensor.INACTIVE)
            appearance = SignalHead.RED;
        if (watchSensor3!=null && watchSensor3.getBean().getKnownState() != Sensor.INACTIVE)
            appearance = SignalHead.RED;
        if (watchSensor4!=null && watchSensor4.getBean().getKnownState() != Sensor.INACTIVE)
            appearance = SignalHead.RED;

        if (watchTurnout!=null && watchTurnout.getBean().getKnownState() != Turnout.CLOSED)
            appearance = SignalHead.RED;
        if (watchTurnout!=null && watchTurnout.getBean().getCommandedState() != Turnout.CLOSED)
            appearance = SignalHead.RED;

        // check if signal if held, forcing a red aspect by this calculation
        if (((SignalHead)outputs[0]).getHeld())
            appearance = SignalHead.RED;

        // handle approach lighting
        doApproach();

        // show result if changed
        if (appearance != oldAppearance) {
            ((SignalHead)outputs[0]).setAppearance(appearance);
            log.debug("Change appearance of {} to {}", name, appearance);
        }
    }

On Diverging Leg of Trailing-Point Turnout

This signal is along the diverging route through a turnout, which is defined as the direction taken by trains when the turnout is set to "thrown". It's protecting the frog of the turnout so that trains will stop before running through a turnout set against them.

    void doTrailingDiverging() {
        int appearance = SignalHead.GREEN;
        int oldAppearance = ((SignalHead)outputs[0]).getAppearance();
        // check for yellow, flashing yellow overriding green
        if (protectWithFlashing && fastestColor1()==SignalHead.YELLOW)
            appearance = SignalHead.FLASHYELLOW;
        if (fastestColor1()==SignalHead.RED || fastestColor1()==SignalHead.FLASHRED)
            appearance = SignalHead.YELLOW;

        // if distant signal, show exactly what the home signal does
        if (distantSignal)
            appearance = fastestColor1();

        // if limited speed and green, reduce to yellow
        if (limitSpeed2)
            appearance = slowerOf(appearance, SignalHead.YELLOW);

        // check for red overriding yellow or green
        if (watchSensor1!=null && watchSensor1.getBean().getKnownState() != Sensor.INACTIVE)
            appearance = SignalHead.RED;
        if (watchSensor2!=null && watchSensor2.getBean().getKnownState() != Sensor.INACTIVE)
            appearance = SignalHead.RED;
        if (watchSensor3!=null && watchSensor3.getBean().getKnownState() != Sensor.INACTIVE)
            appearance = SignalHead.RED;
        if (watchSensor4!=null && watchSensor4.getBean().getKnownState() != Sensor.INACTIVE)
            appearance = SignalHead.RED;

        if (watchTurnout!=null && watchTurnout.getBean().getKnownState() != Turnout.THROWN)
            appearance = SignalHead.RED;
        if (watchTurnout!=null && watchTurnout.getBean().getCommandedState() != Turnout.THROWN)
            appearance = SignalHead.RED;

        // check if signal if held, forcing a red aspect by this calculation
        if (((SignalHead)outputs[0]).getHeld())
            appearance = SignalHead.RED;

        // handle approach lighting
        doApproach();

        // show result if changed
        if (appearance != oldAppearance) {
            ((SignalHead)outputs[0]).setAppearance(appearance);
            if (log.isDebugEnabled()) log.debug("Change appearance of "+name+" to "+appearance);
        }
    }

On Facing-Point Turnout

This signal is protecting the points-end of a turnout. Depending on whether the turnout is thrown or closed, the train will take two different routes, and the signal will protect different next blocks.

    void doFacing() {
        int appearance = SignalHead.GREEN;
        int oldAppearance = ((SignalHead)outputs[0]).getAppearance();

        // find downstream appearance, being pessimistic if we're not sure of the state
        int s = SignalHead.GREEN;
        if (watchTurnout!=null && watchTurnout.getBean().getKnownState() != Turnout.THROWN)
            s = slowerOf(s, fastestColor1());
        if (watchTurnout!=null && watchTurnout.getBean().getKnownState() != Turnout.CLOSED)
            s = slowerOf(s, fastestColor2());

        // check for yellow, flashing yellow overriding green
        if (protectWithFlashing && s==SignalHead.YELLOW)
            appearance = SignalHead.FLASHYELLOW;
        if (s==SignalHead.RED  || s==SignalHead.FLASHRED)
            appearance = SignalHead.YELLOW;
        // if distant signal, show exactly what the home signal does
        if (distantSignal)
            appearance = s;

        // if limited speed and green or flashing yellow, reduce to yellow
        if (watchTurnout!=null && limitSpeed1 && watchTurnout.getBean().getKnownState()!=Turnout.THROWN)
            appearance = slowerOf(appearance, SignalHead.YELLOW);

        if (watchTurnout!=null && limitSpeed2 && watchTurnout.getBean().getKnownState()!=Turnout.CLOSED)
            appearance = slowerOf(appearance, SignalHead.YELLOW);


        // check for red overriding yellow or green
        if (watchSensor1!=null && watchSensor1.getBean().getKnownState() != Sensor.INACTIVE)
            appearance = SignalHead.RED;
        if (watchSensor2!=null && watchSensor2.getBean().getKnownState() != Sensor.INACTIVE)
            appearance = SignalHead.RED;
        if (watchSensor3!=null && watchSensor3.getBean().getKnownState() != Sensor.INACTIVE)
            appearance = SignalHead.RED;
        if (watchSensor4!=null && watchSensor4.getBean().getKnownState() != Sensor.INACTIVE)
            appearance = SignalHead.RED;

        if ((watchTurnout!=null && watchTurnout.getBean().getKnownState() == Turnout.CLOSED)
                && ((watchedSensor1!=null && watchedSensor1.getBean().getKnownState() != Sensor.INACTIVE)))
            appearance = SignalHead.RED;
        if ((watchTurnout!=null && watchTurnout.getBean().getKnownState() == Turnout.CLOSED) && ((watchedSensor1Alt!=null && watchedSensor1Alt.getBean().getKnownState() != Sensor.INACTIVE)))
            appearance = SignalHead.RED;
        if ((watchTurnout!=null && watchTurnout.getBean().getKnownState() == Turnout.THROWN) && ((watchedSensor2!=null && watchedSensor2.getBean().getKnownState() != Sensor.INACTIVE)))
            appearance = SignalHead.RED;
        if ((watchTurnout!=null && watchTurnout.getBean().getKnownState() == Turnout.THROWN) && ((watchedSensor2Alt!=null && watchedSensor2Alt.getBean().getKnownState() != Sensor.INACTIVE)))
            appearance = SignalHead.RED;

        // check if turnout in motion, if so force red
        if (watchTurnout!=null && (watchTurnout.getBean().getKnownState() != watchTurnout.getBean().getCommandedState()) )
            appearance = SignalHead.RED;
        if (watchTurnout!=null && (watchTurnout.getBean().getKnownState() != Turnout.THROWN) && (watchTurnout.getBean().getKnownState() != Turnout.CLOSED) )  // checking for other states
            appearance = SignalHead.RED;

        // check if signal if held, forcing a red aspect by this calculation
        if (((SignalHead)outputs[0]).getHeld())
            appearance = SignalHead.RED;

        // handle approach lighting
        doApproach();

        // show result if changed
        if (appearance != oldAppearance)
            ((SignalHead)outputs[0]).setAppearance(appearance);
    }