content: /**
   * Container for link between two Osm nodes
   *
   * @author ab
   */
  package btools.router;
  
  import java.util.Map;
  
  import btools.expressions.BExpressionContextNode;
  import btools.expressions.BExpressionContextWay;
  
  
  final class KinematicModel extends OsmPathModel {
    public OsmPrePath createPrePath() {
      return new KinematicPrePath();
    }
  
    public OsmPath createPath() {
      return new KinematicPath();
    }
  
    public double turnAngleDecayTime;
    public double f_roll;
    public double f_air;
    public double f_recup;
    public double p_standby;
    public double outside_temp;
    public double recup_efficiency;
    public double totalweight;
    public double vmax;
    public double leftWaySpeed;
    public double rightWaySpeed;
  
    // derived values
    public double pw; // balance power
    public double cost0; // minimum possible cost per meter
  
    private int wayIdxMaxspeed;
    private int wayIdxMaxspeedExplicit;
    private int wayIdxMinspeed;
  
    private int nodeIdxMaxspeed;
  
    protected BExpressionContextWay ctxWay;
    protected BExpressionContextNode ctxNode;
    protected Map<String, String> params;
  
    private boolean initDone = false;
  
    private double lastEffectiveLimit;
    private double lastBreakingSpeed;
  
    @Override
    public void init(BExpressionContextWay expctxWay, BExpressionContextNode expctxNode, Map<String, String> extraParams) {
      if (!initDone) {
        ctxWay = expctxWay;
        ctxNode = expctxNode;
        wayIdxMaxspeed = ctxWay.getOutputVariableIndex("maxspeed", false);
        wayIdxMaxspeedExplicit = ctxWay.getOutputVariableIndex("maxspeed_explicit", false);
        wayIdxMinspeed = ctxWay.getOutputVariableIndex("minspeed", false);
        nodeIdxMaxspeed = ctxNode.getOutputVariableIndex("maxspeed", false);
        initDone = true;
      }
  
      params = extraParams;
  
      turnAngleDecayTime = getParam("turnAngleDecayTime", 5.f);
      f_roll = getParam("f_roll", 232.f);
      f_air = getParam("f_air", 0.4f);
      f_recup = getParam("f_recup", 400.f);
      p_standby = getParam("p_standby", 250.f);
      outside_temp = getParam("outside_temp", 20.f);
      recup_efficiency = getParam("recup_efficiency", 0.7f);
      totalweight = getParam("totalweight", 1640.f);
      vmax = getParam("vmax", 80.f) / 3.6;
      leftWaySpeed = getParam("leftWaySpeed", 12.f) / 3.6;
      rightWaySpeed = getParam("rightWaySpeed", 12.f) / 3.6;
  
      pw = 2. * f_air * vmax * vmax * vmax - p_standby;
      cost0 = (pw + p_standby) / vmax + f_roll + f_air * vmax * vmax;
    }
  
    protected float getParam(String name, float defaultValue) {
      String sval = params == null ? null : params.get(name);
      if (sval != null) {
        return Float.parseFloat(sval);
      }
      float v = ctxWay.getVariableValue(name, defaultValue);
      if (params != null) {
        params.put(name, "" + v);
      }
      return v;
    }
  
    public float getWayMaxspeed() {
      return ctxWay.getBuildInVariable(wayIdxMaxspeed) / 3.6f;
    }
  
    public float getWayMaxspeedExplicit() {
      return ctxWay.getBuildInVariable(wayIdxMaxspeedExplicit) / 3.6f;
    }
  
    public float getWayMinspeed() {
      return ctxWay.getBuildInVariable(wayIdxMinspeed) / 3.6f;
    }
  
    public float getNodeMaxspeed() {
      return ctxNode.getBuildInVariable(nodeIdxMaxspeed) / 3.6f;
    }
  
    /**
     * get the effective speed limit from the way-limit and vmax/vmin
     */
    public double getEffectiveSpeedLimit() {
      // performance related inline coding
      double minspeed = getWayMinspeed();
      double espeed = minspeed > vmax ? minspeed : vmax;
      double maxspeed = getWayMaxspeed();
      return maxspeed < espeed ? maxspeed : espeed;
    }
  
    /**
     * get the breaking speed for current balance-power (pw) and effective speed limit (vl)
     */
    public double getBreakingSpeed(double vl) {
      if (vl == lastEffectiveLimit) {
        return lastBreakingSpeed;
      }
  
      double v = vl * 0.8;
      double pw2 = pw + p_standby;
      double e = recup_efficiency;
      double x0 = pw2 / vl + f_air * e * vl * vl + (1. - e) * f_roll;
      for (int i = 0; i < 5; i++) {
        double v2 = v * v;
        double x = pw2 / v + f_air * e * v2 - x0;
        double dx = 2. * e * f_air * v - pw2 / v2;
        v -= x / dx;
      }
      lastEffectiveLimit = vl;
      lastBreakingSpeed = v;
  
      return v;
    }
  
  }
  