ChannelTaskTrafficLight.java

package org.opentrafficsim.road.gtu.perception.mental.channel;

import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.function.Function;

import org.djunits.value.vdouble.scalar.Duration;
import org.opentrafficsim.base.DistancedObject;
import org.opentrafficsim.base.OtsRuntimeException;
import org.opentrafficsim.base.parameters.ParameterTypeDuration;
import org.opentrafficsim.core.gtu.Stateless;
import org.opentrafficsim.core.gtu.perception.EgoPerception;
import org.opentrafficsim.road.gtu.perception.LanePerception;
import org.opentrafficsim.road.gtu.perception.RelativeLane;
import org.opentrafficsim.road.gtu.perception.categories.IntersectionPerception;
import org.opentrafficsim.road.gtu.perception.mental.AbstractTask;
import org.opentrafficsim.road.gtu.perception.mental.ar.ArTaskCarFollowingExp;
import org.opentrafficsim.road.network.object.trafficlight.TrafficLight;

/**
 * Task demand for traffic lights. This is defined as {@code exp(-T/h)} where {@code T} is the time headway to the traffic light
 * and {@code h} is the car-following task parameter that scales it.
 * <p>
 * Copyright (c) 2024-2026 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
 * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
 * </p>
 * @author Wouter Schakel
 */
public class ChannelTaskTrafficLight extends AbstractTask implements ChannelTask, Stateless<ChannelTaskTrafficLight>
{

    /** Car-following task parameter. */
    public static final ParameterTypeDuration HEXP = ArTaskCarFollowingExp.HEXP;

    /** Singleton instance. */
    public static final ChannelTaskTrafficLight SINGLETON = new ChannelTaskTrafficLight();

    /** Default set that is returned by the supplier. */
    private static final Set<ChannelTask> SET = Set.of(SINGLETON);

    /** Standard supplier that supplies a single instance of the traffic light task. */
    public static final Function<LanePerception, Set<ChannelTask>> SUPPLIER = (p) -> SET;

    /**
     * Constructor.
     */
    public ChannelTaskTrafficLight()
    {
        super("traffic-light (front)");
    }

    @Override
    public ChannelTaskTrafficLight get()
    {
        return SINGLETON;
    }

    @Override
    public Object getChannel()
    {
        return SINGLETON;
    }

    @Override
    public double calculateTaskDemand(final LanePerception perception)
    {
        IntersectionPerception intersection = perception.getPerceptionCategoryOptional(IntersectionPerception.class)
                .orElseThrow(() -> new NoSuchElementException("IntersectionPerception not present."));
        Iterator<DistancedObject<TrafficLight>> trafficLights =
                intersection.getTrafficLights(RelativeLane.CURRENT).underlyingWithDistance();
        if (!trafficLights.hasNext())
        {
            return 0.0;
        }
        EgoPerception<?, ?> ego = perception.getPerceptionCategoryOptional(EgoPerception.class)
                .orElseThrow(() -> new NoSuchElementException("EgoPerception not present."));
        Duration headway = trafficLights.next().distance().divide(ego.getSpeed());
        Duration h = perception.getGtu().getParameters().getOptionalParameter(HEXP)
                .orElseThrow(() -> new OtsRuntimeException("Parameter h_exp not present."));
        return Math.exp(-headway.si / h.si);
    }

}