# 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);
        }