001package jmri.jmrit.logix;
002
003import java.util.ArrayList;
004import java.util.ListIterator;
005
006/**
007 * This class holds a list of throttle setting to make a smooth acceleration or
008 * deceleration. It supplies iterators to cycle through the settings.
009 * Used when speed changes are called for by signaled speeds, block speed limits
010 * or user controls for speed halts and resumes.  Also used to make NXWarrants.
011 *  
012 * @author Pete Cressman Copyright (C) 2019
013*/
014
015public class RampData {
016
017    private final int _timeInterval;
018    private final float _throttleInterval;
019    private ArrayList<Float> _settings;
020    private boolean _upRamp;
021    private final SpeedUtil _speedUtil;
022    private final float _fromSpeed;
023    private final float _toSpeed;
024
025    private static final float INCRE_RATE = 1.085f;  // multiplier to increase throttle increments
026
027    RampData(SpeedUtil util, float throttleIncre, int timeIncre, float fromSet, float toSet) {
028        _throttleInterval = throttleIncre; 
029        _timeInterval = timeIncre;
030        _speedUtil = util;
031        _fromSpeed = fromSet;
032        _toSpeed = toSet;
033        makeThrottleSettings();
034    }
035    
036    protected boolean isUpRamp() {
037        return _upRamp;
038    }
039
040    private void makeThrottleSettings() {
041        _upRamp = (_toSpeed >= _fromSpeed);
042        _settings = new ArrayList<>();
043        float lowSetting;
044        float highSetting;
045        float momentumTime;
046        if (_upRamp) {
047            lowSetting = _fromSpeed;
048            highSetting = _toSpeed;
049        } else {
050            lowSetting = _toSpeed;
051            highSetting = _fromSpeed;
052        }
053        float low = 0.0f;
054        float throttleIncre = _throttleInterval;
055        while (low < lowSetting ) {
056            throttleIncre *= INCRE_RATE;
057            low += throttleIncre;
058        }
059        _settings.add(lowSetting);
060        lowSetting += throttleIncre;
061        while (lowSetting < highSetting) {
062            _settings.add(lowSetting);
063            momentumTime = _speedUtil.getMomentumTime(lowSetting, lowSetting + throttleIncre*INCRE_RATE);
064            if (momentumTime <= _timeInterval) {
065                throttleIncre *= INCRE_RATE;
066            }  // if time of momentum change exceeds _throttleInterval, don't increase throttleIncre
067            lowSetting += throttleIncre;
068        }
069        _settings.add(highSetting);
070    }
071
072    protected float getRampLength() {
073        float rampLength = 0;
074        float nextSetting;
075        float prevSetting;
076        float momentumTime = 0;
077        float dist;
078        if (_upRamp) {
079            ListIterator<Float> iter = speedIterator(true);
080            prevSetting = iter.next(); // first setting is current speed
081            nextSetting = prevSetting;
082            while (iter.hasNext()) {
083                nextSetting = iter.next();
084                dist = _speedUtil.getDistanceOfSpeedChange(prevSetting, nextSetting, _timeInterval);
085                rampLength += dist;
086                momentumTime = _speedUtil.getMomentumTime(prevSetting, nextSetting);
087                prevSetting = nextSetting;
088            }
089        } else {
090            ListIterator<Float> iter = speedIterator(false);
091            prevSetting = iter.previous(); // first setting is current speed
092            nextSetting = prevSetting;
093            while (iter.hasPrevious()) {
094                nextSetting = iter.previous();
095                dist = _speedUtil.getDistanceOfSpeedChange(prevSetting, nextSetting, _timeInterval);
096                rampLength += dist;
097                momentumTime = _speedUtil.getMomentumTime(prevSetting, nextSetting);
098                prevSetting = nextSetting;
099            }
100        }
101        // distance of the last speed increment is only distance needed for momentum.
102        // _speedUtil.getDistanceOfSpeedChange will not return a distance greater than that needed by momentum
103        if (_timeInterval > momentumTime) {
104            rampLength -= _speedUtil.getTrackSpeed(nextSetting) * (_timeInterval - momentumTime);  
105        }
106        return rampLength;
107    }
108
109    protected int getNumSteps() {
110        return _settings.size() - 1;
111    }
112
113    protected int getRamptime() {
114        return (_settings.size() - 1) * _timeInterval;
115    }
116
117    protected float getMaxSpeed() {
118        if (_settings == null) {
119            throw new IllegalArgumentException("Null array of throttle settings"); 
120        }
121        return _settings.get(_settings.size() - 1);
122    }
123
124    protected ListIterator<Float> speedIterator(boolean up) {
125        if (up) {
126            return _settings.listIterator(0);
127        } else {
128            return _settings.listIterator(_settings.size());
129            
130        }
131    }
132
133    protected int getRampTimeIncrement() {
134        return _timeInterval;
135    }
136
137//    private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(RampData.class);
138}