Skip to content

Commit

Permalink
core: choose arrivalAt/departureFrom in envelope interpolation
Browse files Browse the repository at this point in the history
interpolateTotalTime() is removed, in favor of one of:
* interpolateDepartureFrom() (which was the previous behavior)
* interpolateArrivalAt()

also:
* little cleanup on clamp
  • Loading branch information
bougue-pe committed Jun 19, 2024
1 parent 09e8d71 commit f328a62
Show file tree
Hide file tree
Showing 23 changed files with 211 additions and 127 deletions.
56 changes: 37 additions & 19 deletions core/envelope-sim/src/main/java/fr/sncf/osrd/envelope/Envelope.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import static fr.sncf.osrd.envelope_utils.DoubleUtils.clamp;

import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import fr.sncf.osrd.envelope.part.EnvelopePart;
import fr.sncf.osrd.reporting.exceptions.ErrorType;
Expand Down Expand Up @@ -184,27 +185,48 @@ public double maxSpeedInRange(double beginPos, double endPos) {
return maxSpeed;
}

/** Computes the time required to get to a given point of the envelope */
public long interpolateTotalTimeUS(double position) {
private long interpolateUS(double position, boolean isArrivalAt) {
assert continuous : "interpolating times on a non continuous envelope is a risky business";
var envelopePartIndex = findLeft(position);
assert envelopePartIndex >= 0 : "Trying to interpolate time outside of the envelope";
var envelopePart = get(envelopePartIndex);
return getCumulativeTimeUS(envelopePartIndex) + envelopePart.interpolateTotalTimeUS(position);
}

/** Computes the time required to get to a given point of the envelope */
public double interpolateTotalTime(double position) {
return ((double) interpolateTotalTimeUS(position)) / 1_000_000;
@Override
public double interpolateArrivalAt(double position) {
return ((double) interpolateArrivalAtUS(position)) / 1_000_000;
}

/**
* Computes the time required to get to a given point of the envelope. The value is clamped to
* the [0, envelope length] range.
*/
public double interpolateTotalTimeClamp(double position) {
position = Math.min(getEndPos(), Math.max(0, position));
return ((double) interpolateTotalTimeUS(position)) / 1_000_000;
@Override
public long interpolateArrivalAtUS(double position) {
return interpolateUS(position, true);
}

@Override
public double interpolateDepartureFrom(double position) {
return ((double) interpolateDepartureFromUS(position)) / 1_000_000;
}

@Override
public long interpolateDepartureFromUS(double position) {
return interpolateUS(position, false);
}

@Override
public double interpolateArrivalAtClamp(double position) {
return ((double) interpolateArrivalAtUS(clamp(position, 0, getEndPos()))) / 1_000_000;
}

@Override
public double interpolateDepartureFromClamp(double position) {
return ((double) interpolateDepartureFromUS(clamp(position, 0, getEndPos()))) / 1_000_000;
}

/** Returns the time between the two positions of the envelope
* (no stop included in envelope, so no problem) */
public double getTimeBetween(double beginPos, double endPos) {
return interpolateDepartureFrom(endPos) - interpolateDepartureFrom(beginPos);
}

// endregion
Expand Down Expand Up @@ -238,11 +260,6 @@ public double getTotalTime() {
return ((double) getTotalTimeUS()) / 1_000_000;
}

/** Returns the time between two positions of the envelope */
public double getTimeBetween(double beginPos, double endPos) {
return interpolateTotalTime(endPos) - interpolateTotalTime(beginPos);
}

/**
* Returns the total time required to get from the start of the envelope to the start of an
* envelope part, in microseconds
Expand All @@ -261,7 +278,7 @@ public long getCumulativeTimeUS(int transitionIndex) {
/**
* Cuts an envelope, interpolating new points if required.
*
* @return a list of envelope parts spanning from beginPosition to endPosition
* @return an array of envelope parts spanning from beginPosition to endPosition
*/
public EnvelopePart[] slice(double beginPosition, double endPosition) {
return slice(beginPosition, Double.NaN, endPosition, Double.NaN);
Expand All @@ -270,7 +287,7 @@ public EnvelopePart[] slice(double beginPosition, double endPosition) {
/**
* Cuts an envelope, interpolating new points if required.
*
* @return a list of envelope parts spanning from beginPosition to endPosition
* @return aa array of envelope parts spanning from beginPosition to endPosition
*/
public EnvelopePart[] slice(double beginPosition, double beginSpeed, double endPosition, double endSpeed) {
int beginIndex = 0;
Expand Down Expand Up @@ -339,6 +356,7 @@ public EnvelopePart[] slice(
// endregion

@Override
@NonNull
public Iterator<EnvelopePart> iterator() {
return new Iterator<>() {
private int i = 0;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package fr.sncf.osrd.envelope;

import static fr.sncf.osrd.envelope_utils.DoubleUtils.clamp;

import java.util.ArrayList;
import java.util.List;

Expand Down Expand Up @@ -47,21 +49,37 @@ private static List<LocatedEnvelope> initLocatedEnvelopes(List<? extends Envelop
}

@Override
public double interpolateTotalTime(double position) {
var envelope = findEnvelopeAt(position);
public double interpolateArrivalAt(double position) {
var envelope = findEnvelopeLeftAt(position);
assert envelope != null : "Trying to interpolate time outside of the envelope";
return envelope.startTime + envelope.envelope.interpolateArrivalAtClamp(position - envelope.startOffset);
}

@Override
public double interpolateDepartureFrom(double position) {
var envelope = findEnvelopeRightAt(position);
assert envelope != null : "Trying to interpolate time outside of the envelope";
return envelope.startTime + envelope.envelope.interpolateTotalTimeClamp(position - envelope.startOffset);
return envelope.startTime + envelope.envelope.interpolateDepartureFromClamp(position - envelope.startOffset);
}

@Override
public long interpolateTotalTimeUS(double position) {
return (long) (interpolateTotalTime(position) * 1_000_000);
public long interpolateArrivalAtUS(double position) {
return (long) (interpolateArrivalAt(position) * 1_000_000);
}

@Override
public double interpolateTotalTimeClamp(double position) {
var clamped = Math.max(0, Math.min(position, endPos));
return interpolateTotalTime(clamped);
public long interpolateDepartureFromUS(double position) {
return (long) (interpolateDepartureFrom(position) * 1_000_000);
}

@Override
public double interpolateArrivalAtClamp(double position) {
return interpolateArrivalAt(clamp(position, 0, endPos));
}

@Override
public double interpolateDepartureFromClamp(double position) {
return interpolateDepartureFrom(clamp(position, 0, endPos));
}

@Override
Expand Down Expand Up @@ -94,15 +112,23 @@ public List<EnvelopePoint> iteratePoints() {
/**
* Returns the envelope at the given position.
*/
private LocatedEnvelope findEnvelopeAt(double position) {
var index = findEnvelopeIndexAt(position);
private LocatedEnvelope findEnvelopeLeftAt(double position) {
var index = findEnvelopeIndexAt(position, true);
return index == -1 ? null : envelopes.get(index);
}

/**
* Returns the envelope at the given position.
*/
private LocatedEnvelope findEnvelopeRightAt(double position) {
var index = findEnvelopeIndexAt(position, false);
return index == -1 ? null : envelopes.get(index);
}

/**
* Returns the index of the envelope at the given position.
*/
private int findEnvelopeIndexAt(double position) {
private int findEnvelopeIndexAt(double position, boolean searchLeft) {
if (position < 0) return -1;
var lowerBound = 0; // included
var upperBound = envelopes.size(); // excluded
Expand All @@ -113,6 +139,12 @@ private int findEnvelopeIndexAt(double position) {
upperBound = i;
} else if (position > envelope.startOffset + envelope.envelope.getEndPos()) {
lowerBound = i + 1;
} else if (searchLeft && position == envelope.startOffset && i > 0) {
return i - 1;
} else if (!searchLeft
&& position == envelope.startOffset + envelope.envelope.getEndPos()
&& i < envelopes.size() - 1) {
return i + 1;
} else {
return i;
}
Expand All @@ -122,13 +154,13 @@ private int findEnvelopeIndexAt(double position) {

@Override
public double maxSpeedInRange(double beginPos, double endPos) {
var firstEnvelopeIndex = findEnvelopeIndexAt(beginPos);
var firstEnvelopeIndex = findEnvelopeIndexAt(beginPos, false);
assert firstEnvelopeIndex != -1 : "Trying to interpolate time outside of the envelope";
var firstEnvelope = envelopes.get(firstEnvelopeIndex);
var beginSpeed = firstEnvelope.envelope.maxSpeedInRange(
beginPos - firstEnvelope.startOffset, firstEnvelope.envelope.getEndPos());

var lastEnvelopeIndex = findEnvelopeIndexAt(endPos);
var lastEnvelopeIndex = findEnvelopeIndexAt(endPos, true);
assert lastEnvelopeIndex != -1 : "Trying to interpolate time outside of the envelope";
var lastEnvelope = envelopes.get(lastEnvelopeIndex);
var endOffset = endPos - lastEnvelope.startOffset;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,27 @@
public interface EnvelopeTimeInterpolate {

/** Computes the time required to get to a given point of the envelope */
double interpolateTotalTime(double position);
double interpolateArrivalAt(double position);

/** Computes last time when the train is at a given point of the envelope (including stop duration if at stop) */
double interpolateDepartureFrom(double position);

/** Computes the time required to get to a given point of the envelope in microseconds */
long interpolateTotalTimeUS(double position);
long interpolateArrivalAtUS(double position);

/** Computes last time when the train is at a given point of the envelope (including stop duration if at stop) in microseconds */
long interpolateDepartureFromUS(double position);

/**
* Computes the time required to get to a given point of the envelope, clamping the position to
* [0, envelope length] first
*/
double interpolateTotalTimeClamp(double position);
double interpolateArrivalAtClamp(double position);

/**
* Computes last time when the train is at a given point of the envelope (including stop duration if at stop), clamping the position to [0, envelope length] first
*/
double interpolateDepartureFromClamp(double position);

/** Returns the start position of the envelope */
double getBeginPos();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@ public void testSingleEnvelope() {
// List of functions to call, they should return the same result for the envelope and the
// concatenated version
var functions = List.<Function<EnvelopeTimeInterpolate, Double>>of(
in -> in.interpolateTotalTime(0),
in -> in.interpolateTotalTime(1),
in -> in.interpolateTotalTime(2),
in -> in.interpolateTotalTimeUS(1.5) / 1_000.,
in -> in.interpolateTotalTimeClamp(-1),
in -> in.interpolateTotalTimeClamp(0.5),
in -> in.interpolateDepartureFrom(0),
in -> in.interpolateDepartureFrom(1),
in -> in.interpolateDepartureFrom(2),
in -> in.interpolateDepartureFromUS(1.5) / 1_000.,
in -> in.interpolateDepartureFromClamp(-1),
in -> in.interpolateDepartureFromClamp(0.5),
EnvelopeTimeInterpolate::getBeginPos,
EnvelopeTimeInterpolate::getEndPos,
EnvelopeTimeInterpolate::getTotalTime);
Expand All @@ -46,7 +46,8 @@ public void testTwoEnvelopes() {
final var secondEnvelopeTime = envelopes.get(1).getTotalTime();

assertEquals(
firstEnvelopeTime + envelopes.get(1).interpolateTotalTime(1), concatenated.interpolateTotalTime(3));
firstEnvelopeTime + envelopes.get(1).interpolateDepartureFrom(1),
concatenated.interpolateDepartureFrom(3));
assertEquals(0, concatenated.getBeginPos());
assertEquals(4, concatenated.getEndPos());
assertEquals(firstEnvelopeTime + secondEnvelopeTime, concatenated.getTotalTime());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,18 +55,18 @@ void testInterpolateSpeed() {
}

@Test
void testInterpolateTime() {
void testInterpolateDepartureFrom() {
var partA = EnvelopeTestUtils.generateTimes(new double[] {0, 2}, new double[] {1, 1});
var partB = EnvelopeTestUtils.generateTimes(new double[] {2, 4}, new double[] {1, 1});
var envelope = Envelope.make(partA, partB);

assertEquals(1, partA.interpolateTotalTime(1));

assertEquals(1, envelope.interpolateTotalTime(1));
assertEquals(2, envelope.interpolateTotalTime(2));
assertEquals(2, envelope.interpolateTotalTime(2));
assertEquals(3, envelope.interpolateTotalTime(3));
assertEquals(3.5, envelope.interpolateTotalTime(3.5));
assertEquals(4, envelope.interpolateTotalTime(4));
assertEquals(1, envelope.interpolateDepartureFrom(1));
assertEquals(2, envelope.interpolateDepartureFrom(2));
assertEquals(2, envelope.interpolateDepartureFrom(2));
assertEquals(3, envelope.interpolateDepartureFrom(3));
assertEquals(3.5, envelope.interpolateDepartureFrom(3.5));
assertEquals(4, envelope.interpolateDepartureFrom(4));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -175,16 +175,19 @@ public void testRangesPassageTime() {

// Check that we lose as much time as specified
assertEquals(
maxEffortEnvelope.interpolateTotalTime(rangesTransitions[1]) + value1.time,
marecoEnvelope.interpolateTotalTime(rangesTransitions[1]),
maxEffortEnvelope.interpolateDepartureFrom(rangesTransitions[1]) + value1.time,
marecoEnvelope.interpolateDepartureFrom(rangesTransitions[1]),
testContext.timeStep);
assertEquals(
maxEffortEnvelope.interpolateTotalTime(rangesTransitions[2]) + value1.time + value2.time,
marecoEnvelope.interpolateTotalTime(rangesTransitions[2]),
maxEffortEnvelope.interpolateDepartureFrom(rangesTransitions[2]) + value1.time + value2.time,
marecoEnvelope.interpolateDepartureFrom(rangesTransitions[2]),
testContext.timeStep);
assertEquals(
maxEffortEnvelope.interpolateTotalTime(rangesTransitions[3]) + value1.time + value2.time + value3.time,
marecoEnvelope.interpolateTotalTime(rangesTransitions[3]),
maxEffortEnvelope.interpolateDepartureFrom(rangesTransitions[3])
+ value1.time
+ value2.time
+ value3.time,
marecoEnvelope.interpolateDepartureFrom(rangesTransitions[3]),
testContext.timeStep);
assertEquals(
maxEffortEnvelope.getTotalTime() + value1.time + value2.time + value3.time,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -183,11 +183,11 @@ private void testTransitionPoints(
var endPos = allowance.endPos;
var allowanceEnvelope = allowance.apply(base, context);

var timeBeginPointBase = base.interpolateTotalTime(beginPos);
var timeEndPointBase = base.interpolateTotalTime(endPos);
var timeBeginPointBase = base.interpolateDepartureFrom(beginPos);
var timeEndPointBase = base.interpolateDepartureFrom(endPos);

var timeBeginPoint = allowanceEnvelope.interpolateTotalTime(beginPos);
var timeEndPoint = allowanceEnvelope.interpolateTotalTime(endPos);
var timeBeginPoint = allowanceEnvelope.interpolateDepartureFrom(beginPos);
var timeEndPoint = allowanceEnvelope.interpolateDepartureFrom(endPos);
var expectedTimeEndPoint = timeEndPointBase + allowance.getAddedTime(base);

// make sure begin has the same time before and after margin, and that end is offset by the
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,12 @@ class IncrementalRequirementEnvelopeAdapter(
if (stopOffset.distance.meters > endPos) {
return Double.POSITIVE_INFINITY
}
// stop duration is included in interpolateTotalTime()
// stop duration is included in interpolateDepartureFrom()
var pastStop = (stopOffset.distance).meters
if (pastStop > endPos) {
pastStop = endPos
}
return envelopeWithStops.interpolateTotalTime(pastStop)
return envelopeWithStops.interpolateDepartureFrom(pastStop)
}

override fun arrivalTimeInRange(
Expand All @@ -54,7 +54,7 @@ class IncrementalRequirementEnvelopeAdapter(
// if the head of the train enters the zone at some point, use that
val begin = pathBeginOff.distance.meters
if (begin >= 0.0 && begin <= envelopeWithStops.endPos)
return envelopeWithStops.interpolateTotalTime(begin)
return envelopeWithStops.interpolateArrivalAt(begin)

val end = pathEndOff.distance.meters

Expand All @@ -75,7 +75,7 @@ class IncrementalRequirementEnvelopeAdapter(

val criticalPoint = end + rollingStock.length
if (criticalPoint >= 0.0 && criticalPoint <= envelopeWithStops.endPos)
return envelopeWithStops.interpolateTotalTime(criticalPoint)
return envelopeWithStops.interpolateDepartureFrom(criticalPoint)

return Double.POSITIVE_INFINITY
}
Expand Down
Loading

0 comments on commit f328a62

Please sign in to comment.