Skip to content

Commit

Permalink
Merge pull request #13112 from Pugwash1/20240521-SpeedProfile
Browse files Browse the repository at this point in the history
Set speed directly when distance, speed or profile values do not allo…
  • Loading branch information
danielb987 committed May 23, 2024
2 parents ec9bfc4 + f234d1f commit bb610da
Show file tree
Hide file tree
Showing 3 changed files with 278 additions and 5 deletions.
2 changes: 1 addition & 1 deletion java/src/jmri/jmrit/dispatcher/AutoActiveTrain.java
Original file line number Diff line number Diff line change
Expand Up @@ -1957,7 +1957,7 @@ public void setHalt(boolean halt) {
}

public void setTargetSpeed(float distance, float speed) {
log.debug("Set Target Speed[{}] with distance{{}]",speed,distance);
log.debug("Set Target Speed[{}] with distance{{}] from speed[{}]",speed,distance,throttle.getSpeedSetting());
stopAllTimers();
if (rosterEntry != null) {
rosterEntry.getSpeedProfile().setExtraInitialDelay(1500f);
Expand Down
31 changes: 29 additions & 2 deletions java/src/jmri/jmrit/roster/RosterSpeedProfile.java
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
package jmri.jmrit.roster;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Map.Entry;
import java.util.TreeMap;
import jmri.Block;
import jmri.DccThrottle;
import jmri.NamedBean;
import jmri.Section;
import jmri.implementation.SignalSpeedMap;

import org.jdom2.Element;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand Down Expand Up @@ -69,6 +72,24 @@ public boolean hasReverseSpeeds() {
return _hasReverseSpeeds;
}

/**
* place / remove SpeedProfile from test mode.
* reinitializes speedstep trace array
* @param value true/false
*/
protected void setTestMode(boolean value) {
profileInTestMode = value;
testSteps = new ArrayList<SpeedSetting>();
}

/**
* Gets the speed step trace array.
* @return speedstep trace array
*/
protected List<SpeedSetting> getSpeedStepTrace() {
return testSteps;
}

/* for speed conversions */
static public final float MMS_TO_MPH = 0.00223694f;
static public final float MMS_TO_KPH = 0.0036f;
Expand Down Expand Up @@ -697,6 +718,9 @@ public void changeLocoSpeed(DccThrottle t, float distance, float speed) {

int extraTime = 0;

private List<SpeedSetting> testSteps = new ArrayList<SpeedSetting>();
private boolean profileInTestMode = false;

void calculateStepDetails(float speedStep, float distance) {

float stepIncrement = _throttle.getSpeedIncrement();
Expand Down Expand Up @@ -817,6 +841,9 @@ void calculateStepDetails(float speedStep, float distance) {
SpeedSetting ss = new SpeedSetting(calculatingStep, timePerStep);
synchronized (this) {
stepQueue.addLast(ss);
if (profileInTestMode) {
testSteps.add(ss);
}
}
if (stopTimer == null) { //If this is the first time round then kick off the speed change
setNextStep();
Expand All @@ -830,7 +857,7 @@ void calculateStepDetails(float speedStep, float distance) {
calculatedDistance = 0;
}
if (calculatedDistance <= 0 && !calculated) {
log.error("distance remaining is now 0, but we have not reached desired speed setting {} v {}", desiredSpeedStep, calculatingStep);
log.warn("distance remaining is now 0, but we have not reached desired speed setting {} v {}", desiredSpeedStep, calculatingStep);
ss = new SpeedSetting(desiredSpeedStep, 10);
synchronized (this) {
stepQueue.addLast(ss);
Expand Down Expand Up @@ -891,7 +918,7 @@ synchronized void setNextStep() {
}
SpeedSetting ss = stepQueue.getFirst();
if (ss.getDuration() == 0) {
_throttle.setSpeedSetting(0);
_throttle.setSpeedSetting(desiredSpeedStep);
finishChange();
return;
}
Expand Down
250 changes: 248 additions & 2 deletions java/test/jmri/jmrit/roster/RosterSpeedProfileTest.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
package jmri.jmrit.roster;

import jmri.util.JUnitUtil;
import jmri.DccThrottle;
import jmri.InstanceManager;
import jmri.LocoAddress;
import jmri.ThrottleListener;
import jmri.ThrottleManager;
import jmri.jmrit.roster.RosterSpeedProfile.SpeedSetting;

import org.junit.Assert;
import org.junit.jupiter.api.*;
Expand All @@ -11,22 +17,262 @@
*/
public class RosterSpeedProfileTest {

boolean throttleResult;
DccThrottle throttle;
protected class ThrottleListen implements ThrottleListener {

@Override
public void notifyThrottleFound(DccThrottle t){
throttleResult = true;
throttle = t;
}

@Override
public void notifyFailedThrottleRequest(LocoAddress address, String reason){
throttleResult = false;
}

@Override
public void notifyDecisionRequired(LocoAddress address, DecisionType question) {
if ( question == DecisionType.STEAL ){
throttleResult = false;
}
}
}

@Test
public void testCTor() {
RosterSpeedProfile t = new RosterSpeedProfile(new RosterEntry());
Assert.assertNotNull("exists",t);
}

@Test
public void testSpeedProfileStopFromFiftyPercent() {
// statics for test objects
org.jdom2.Element f1 = null;
RosterEntry rF1 = null;

// create Element
f1 = new org.jdom2.Element("locomotive")
.setAttribute("id", "id info")
.setAttribute("fileName", "file here")
.setAttribute("roadNumber", "431")
.setAttribute("roadName", "SP")
.setAttribute("mfg", "Athearn")
.setAttribute("dccAddress", "1234")
.addContent(new org.jdom2.Element("decoder")
.setAttribute("family", "91")
.setAttribute("model", "33")
)
.addContent(new org.jdom2.Element("locoaddress")
.addContent(new org.jdom2.Element("number").addContent("1234"))
//As there is no throttle manager available all protocols default to dcc short
.addContent(new org.jdom2.Element("protocol").addContent("dcc_short"))
)
.addContent(new org.jdom2.Element("speedprofile")
.addContent(new org.jdom2.Element("overRunTimeForward").addContent("0.0"))
.addContent(new org.jdom2.Element("overRunTimeReverse").addContent("0.0"))
.addContent(new org.jdom2.Element("speeds")
.addContent(new org.jdom2.Element("speed")
.addContent(new org.jdom2.Element("step").addContent("1000"))
.addContent(new org.jdom2.Element("forward").addContent("100.00"))
.addContent(new org.jdom2.Element("reverse").addContent("100.00"))
)
)
);
rF1 = new RosterEntry(f1) {
@Override
protected void warnShortLong(String s) {
}
};
ThrottleListener throtListen = new ThrottleListen();
ThrottleManager tm = InstanceManager.getDefault(ThrottleManager.class);
boolean OK = tm.requestThrottle(rF1, throtListen, throttleResult);
Assert.assertTrue("Throttle request denied",OK);
JUnitUtil.waitFor(()-> (throttleResult), "Got No throttle");
throttle.setIsForward(true);
throttle.setSpeedSetting(0.5f);
RosterSpeedProfile sp = rF1.getSpeedProfile();
sp.setTestMode(true);
sp.changeLocoSpeed(throttle, 50.0f, 0.0f);
// Allow speed step table to be constructed
//JUnitUtil.waitFor(5000);
// Note it must be a perfect 0.0
//Assert.assertEquals("Speed didnt get to a perfect zero", 0.0f, throttle.getSpeedSetting(), 0.0f);
JUnitUtil.waitFor(()->(throttle.getSpeedSetting() == 0.00f),"Failed to reach requested speed");
float maxDelta = 1.0f/126.0f/2.0f; //half step
Assert.assertEquals("SpeedStep Table has lincorrect number of entries.", 7, sp.getSpeedStepTrace().size() ) ;
int[] correctDuration = {750, 750, 750, 750, 750, 519, 0} ;
int[] durations = new int[sp.getSpeedStepTrace().size()];
int ix = 0;
for (SpeedSetting ss: sp.getSpeedStepTrace()) {
durations[ix]= ss.getDuration();
ix++;
}
Assert.assertArrayEquals("Durations are wrong",correctDuration, durations);

float[] correctSpeed = {0.30798f, 0.17571f, 0.09067f, 0.04558f, 0.02260f, 0.01466f, 0.0f} ;
float[] speed = new float[sp.getSpeedStepTrace().size()];
ix=0;
for (SpeedSetting ss: sp.getSpeedStepTrace()) {
speed[ix]= ss.getSpeedStep();
ix++;
}
Assert.assertArrayEquals("Speeds are wrong", correctSpeed, speed, maxDelta);
sp.cancelSpeedChange();
}

@Test
public void testSpeedProfileFromFiftyPercentToTwenty() {
// statics for test objects
org.jdom2.Element f1 = null;
RosterEntry rF1 = null;

// create Element
f1 = new org.jdom2.Element("locomotive")
.setAttribute("id", "id info")
.setAttribute("fileName", "file here")
.setAttribute("roadNumber", "431")
.setAttribute("roadName", "SP")
.setAttribute("mfg", "Athearn")
.setAttribute("dccAddress", "1234")
.addContent(new org.jdom2.Element("decoder")
.setAttribute("family", "91")
.setAttribute("model", "33")
)
.addContent(new org.jdom2.Element("locoaddress")
.addContent(new org.jdom2.Element("number").addContent("1234"))
//As there is no throttle manager available all protocols default to dcc short
.addContent(new org.jdom2.Element("protocol").addContent("dcc_short"))
)
.addContent(new org.jdom2.Element("speedprofile")
.addContent(new org.jdom2.Element("overRunTimeForward").addContent("0.0"))
.addContent(new org.jdom2.Element("overRunTimeReverse").addContent("0.0"))
.addContent(new org.jdom2.Element("speeds")
.addContent(new org.jdom2.Element("speed")
.addContent(new org.jdom2.Element("step").addContent("1000"))
.addContent(new org.jdom2.Element("forward").addContent("200.00"))
.addContent(new org.jdom2.Element("reverse").addContent("200.00"))
)
)
);
rF1 = new RosterEntry(f1) {
@Override
protected void warnShortLong(String s) {
}
};
ThrottleListener throtListen = new ThrottleListen();
ThrottleManager tm = InstanceManager.getDefault(ThrottleManager.class);
boolean OK = tm.requestThrottle(rF1, throtListen, throttleResult);
Assert.assertTrue("Throttle request denied",OK);

JUnitUtil.waitFor(()-> (throttleResult), "Got No throttle");
throttle.setIsForward(true);
throttle.setSpeedSetting(0.6f);
RosterSpeedProfile sp = rF1.getSpeedProfile();
sp.setTestMode(true);
sp.changeLocoSpeed(throttle, 150.0f, 0.20f);
// Allow speed step table to be constructed
//JUnitUtil.waitFor(5000);
// Note it must be a perfect 0.20
JUnitUtil.waitFor(()->(throttle.getSpeedSetting() == 0.20f),"Failed to reach requested speed");
//Assert.assertEquals("Speed didnt get to a perfect 20", 0.20f, throttle.getSpeedSetting(), 0.00f);
float maxDelta = 1.0f/126.0f/2.0f; //half step
Assert.assertEquals("SpeedStep Table has lincorrect number of entries.", 4, sp.getSpeedStepTrace().size() ) ;
int[] correctDuration = {750, 750, 750, 540} ;
int[] durations = new int[sp.getSpeedStepTrace().size()];
int ix = 0;
for (SpeedSetting ss: sp.getSpeedStepTrace()) {
durations[ix]= ss.getDuration();
ix++;
}
Assert.assertArrayEquals("Durations are wrong",correctDuration, durations);

float[] correctSpeed = {0.43912f, 0.30069f, 0.20311f, 0.2f} ;
float[] speed = new float[sp.getSpeedStepTrace().size()];
ix=0;
for (SpeedSetting ss: sp.getSpeedStepTrace()) {
speed[ix]= ss.getSpeedStep();
ix++;
}
Assert.assertArrayEquals("Speeds are wrong", correctSpeed, speed, maxDelta);
sp.cancelSpeedChange();
}

@Test
public void testSpeedProfileFromFiftyPercentToTwentyShortBlock() {
// statics for test objects
org.jdom2.Element f1 = null;
RosterEntry rF1 = null;

// create Element
f1 = new org.jdom2.Element("locomotive")
.setAttribute("id", "id info")
.setAttribute("fileName", "file here")
.setAttribute("roadNumber", "431")
.setAttribute("roadName", "SP")
.setAttribute("mfg", "Athearn")
.setAttribute("dccAddress", "1234")
.addContent(new org.jdom2.Element("decoder")
.setAttribute("family", "91")
.setAttribute("model", "33")
)
.addContent(new org.jdom2.Element("locoaddress")
.addContent(new org.jdom2.Element("number").addContent("1234"))
//As there is no throttle manager available all protocols default to dcc short
.addContent(new org.jdom2.Element("protocol").addContent("dcc_short"))
)
.addContent(new org.jdom2.Element("speedprofile")
.addContent(new org.jdom2.Element("overRunTimeForward").addContent("0.0"))
.addContent(new org.jdom2.Element("overRunTimeReverse").addContent("0.0"))
.addContent(new org.jdom2.Element("speeds")
.addContent(new org.jdom2.Element("speed")
.addContent(new org.jdom2.Element("step").addContent("200"))
.addContent(new org.jdom2.Element("forward").addContent("40.00"))
.addContent(new org.jdom2.Element("reverse").addContent("40.00"))
)
.addContent(new org.jdom2.Element("speed")
.addContent(new org.jdom2.Element("step").addContent("1000"))
.addContent(new org.jdom2.Element("forward").addContent("400.00"))
.addContent(new org.jdom2.Element("reverse").addContent("400.00"))
)
)
);
rF1 = new RosterEntry(f1) {
@Override
protected void warnShortLong(String s) {
}
};
ThrottleListener throtListen = new ThrottleListen();
ThrottleManager tm = InstanceManager.getDefault(ThrottleManager.class);
boolean OK = tm.requestThrottle(rF1, throtListen, throttleResult);
Assert.assertTrue("Throttle request denied",OK);
JUnitUtil.waitFor(()-> (throttleResult), "Got No throttle");
throttle.setIsForward(true);
throttle.setSpeedSetting(0.6f);
RosterSpeedProfile sp = rF1.getSpeedProfile();
sp.setTestMode(true);
sp.setExtraInitialDelay(1500f);
sp.changeLocoSpeed(throttle, 152.0f, 0.20f);
// Allow speed step table to be constructed
//JUnitUtil.waitFor(3000);
// Note it must be a perfect 0.20
//Assert.assertEquals("Speed didnt get to a perfect zero", 0.20f, throttle.getSpeedSetting(), 0.0f);
JUnitUtil.waitFor(()->(throttle.getSpeedSetting() == 0.20f),"Failed to reach requested speed");

// as the calc goes wrong we immediatly set speed to final speed. The entries are rubbish so dont bother checking
Assert.assertEquals("SpeedStep Table has lincorrect number of entries.", 1, sp.getSpeedStepTrace().size() ) ;
sp.cancelSpeedChange();
}
@BeforeEach
public void setUp() {
JUnitUtil.setUp();
JUnitUtil.initDebugThrottleManager();
}

@AfterEach
public void tearDown() {
JUnitUtil.tearDown();
}

// private final static Logger log = LoggerFactory.getLogger(RosterSpeedProfileTest.class);

}

0 comments on commit bb610da

Please sign in to comment.