diff --git a/core/envelope-sim/src/main/java/fr/sncf/osrd/envelope/Envelope.java b/core/envelope-sim/src/main/java/fr/sncf/osrd/envelope/Envelope.java index 6a0adb1a084..3a744f04995 100644 --- a/core/envelope-sim/src/main/java/fr/sncf/osrd/envelope/Envelope.java +++ b/core/envelope-sim/src/main/java/fr/sncf/osrd/envelope/Envelope.java @@ -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; @@ -184,8 +185,7 @@ 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"; @@ -193,18 +193,40 @@ public long interpolateTotalTimeUS(double position) { 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 @@ -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 @@ -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); @@ -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; @@ -339,6 +356,7 @@ public EnvelopePart[] slice( // endregion @Override + @NonNull public Iterator iterator() { return new Iterator<>() { private int i = 0; diff --git a/core/envelope-sim/src/main/java/fr/sncf/osrd/envelope/EnvelopeConcat.java b/core/envelope-sim/src/main/java/fr/sncf/osrd/envelope/EnvelopeConcat.java index f555995732b..331ffd88a84 100644 --- a/core/envelope-sim/src/main/java/fr/sncf/osrd/envelope/EnvelopeConcat.java +++ b/core/envelope-sim/src/main/java/fr/sncf/osrd/envelope/EnvelopeConcat.java @@ -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; @@ -47,21 +49,37 @@ private static List initLocatedEnvelopes(List 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 @@ -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; } @@ -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; diff --git a/core/envelope-sim/src/main/java/fr/sncf/osrd/envelope/EnvelopeTimeInterpolate.java b/core/envelope-sim/src/main/java/fr/sncf/osrd/envelope/EnvelopeTimeInterpolate.java index a35f85b1662..8b00a99b38a 100644 --- a/core/envelope-sim/src/main/java/fr/sncf/osrd/envelope/EnvelopeTimeInterpolate.java +++ b/core/envelope-sim/src/main/java/fr/sncf/osrd/envelope/EnvelopeTimeInterpolate.java @@ -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(); diff --git a/core/envelope-sim/src/test/java/fr/sncf/osrd/envelope/EnvelopeConcatTest.java b/core/envelope-sim/src/test/java/fr/sncf/osrd/envelope/EnvelopeConcatTest.java index 783a2928e39..85c09827f3b 100644 --- a/core/envelope-sim/src/test/java/fr/sncf/osrd/envelope/EnvelopeConcatTest.java +++ b/core/envelope-sim/src/test/java/fr/sncf/osrd/envelope/EnvelopeConcatTest.java @@ -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.>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); @@ -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()); diff --git a/core/envelope-sim/src/test/java/fr/sncf/osrd/envelope/EnvelopeTest.java b/core/envelope-sim/src/test/java/fr/sncf/osrd/envelope/EnvelopeTest.java index 06ca44259fe..d3992373080 100644 --- a/core/envelope-sim/src/test/java/fr/sncf/osrd/envelope/EnvelopeTest.java +++ b/core/envelope-sim/src/test/java/fr/sncf/osrd/envelope/EnvelopeTest.java @@ -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)); } } diff --git a/core/envelope-sim/src/test/java/fr/sncf/osrd/envelope_sim/AllowanceRangesTests.java b/core/envelope-sim/src/test/java/fr/sncf/osrd/envelope_sim/AllowanceRangesTests.java index 9ae9204aeff..221175b5f39 100644 --- a/core/envelope-sim/src/test/java/fr/sncf/osrd/envelope_sim/AllowanceRangesTests.java +++ b/core/envelope-sim/src/test/java/fr/sncf/osrd/envelope_sim/AllowanceRangesTests.java @@ -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, diff --git a/core/envelope-sim/src/test/java/fr/sncf/osrd/envelope_sim/AllowanceTests.java b/core/envelope-sim/src/test/java/fr/sncf/osrd/envelope_sim/AllowanceTests.java index 0c5003de1a6..9a85635dfe1 100644 --- a/core/envelope-sim/src/test/java/fr/sncf/osrd/envelope_sim/AllowanceTests.java +++ b/core/envelope-sim/src/test/java/fr/sncf/osrd/envelope_sim/AllowanceTests.java @@ -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 diff --git a/core/src/main/java/fr/sncf/osrd/conflicts/IncrementalRequirementEnvelopeAdapter.kt b/core/src/main/java/fr/sncf/osrd/conflicts/IncrementalRequirementEnvelopeAdapter.kt index 26bf46050fd..2762f5777bc 100644 --- a/core/src/main/java/fr/sncf/osrd/conflicts/IncrementalRequirementEnvelopeAdapter.kt +++ b/core/src/main/java/fr/sncf/osrd/conflicts/IncrementalRequirementEnvelopeAdapter.kt @@ -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( @@ -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 @@ -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 } diff --git a/core/src/main/java/fr/sncf/osrd/standalone_sim/EnvelopeStopWrapper.java b/core/src/main/java/fr/sncf/osrd/standalone_sim/EnvelopeStopWrapper.java index dfe0aaf0e2e..455f6761557 100644 --- a/core/src/main/java/fr/sncf/osrd/standalone_sim/EnvelopeStopWrapper.java +++ b/core/src/main/java/fr/sncf/osrd/standalone_sim/EnvelopeStopWrapper.java @@ -1,6 +1,7 @@ package fr.sncf.osrd.standalone_sim; import static fr.sncf.osrd.envelope_sim.TrainPhysicsIntegrator.arePositionsEqual; +import static fr.sncf.osrd.envelope_utils.DoubleUtils.clamp; import fr.sncf.osrd.envelope.EnvelopeInterpolate; import fr.sncf.osrd.train.TrainStop; @@ -18,25 +19,46 @@ public EnvelopeStopWrapper(EnvelopeInterpolate envelope, List stops) this.stops = stops; } - @Override - public double interpolateTotalTime(double position) { + private double interpolate(double position, boolean isArrivalAt) { double stopTime = 0; for (var stop : stops) { - if (stop.position > position && !arePositionsEqual(stop.position, position)) break; + if (arePositionsEqual(stop.position, position)) { + if (isArrivalAt) break; + } else if (position < stop.position) break; stopTime += stop.duration; } - return stopTime + envelope.interpolateTotalTime(position); + return stopTime + + (isArrivalAt ? envelope.interpolateArrivalAt(position) : envelope.interpolateDepartureFrom(position)); + } + + @Override + public double interpolateArrivalAt(double position) { + return interpolate(position, true); + } + + @Override + public double interpolateDepartureFrom(double position) { + return interpolate(position, false); } - /** Interpolate time in microseconds */ - public long interpolateTotalTimeUS(double position) { - return (long) (this.interpolateTotalTime(position) * 1_000_000); + @Override + public long interpolateArrivalAtUS(double position) { + return (long) (this.interpolateArrivalAt(position) * 1_000_000); + } + + @Override + public long interpolateDepartureFromUS(double position) { + return (long) (this.interpolateDepartureFrom(position) * 1_000_000); + } + + @Override + public double interpolateArrivalAtClamp(double position) { + return interpolateArrivalAt(clamp(position, 0, envelope.getEndPos())); } @Override - public double interpolateTotalTimeClamp(double position) { - position = Math.max(0, Math.min(envelope.getEndPos(), position)); - return interpolateTotalTime(position); + public double interpolateDepartureFromClamp(double position) { + return interpolateDepartureFrom(clamp(position, 0, envelope.getEndPos())); } @Override diff --git a/core/src/main/java/fr/sncf/osrd/standalone_sim/ScheduleMetadataExtractor.kt b/core/src/main/java/fr/sncf/osrd/standalone_sim/ScheduleMetadataExtractor.kt index 0e8f19f5195..d2dec2c024b 100644 --- a/core/src/main/java/fr/sncf/osrd/standalone_sim/ScheduleMetadataExtractor.kt +++ b/core/src/main/java/fr/sncf/osrd/standalone_sim/ScheduleMetadataExtractor.kt @@ -111,7 +111,7 @@ fun run( // Compute stops val stops = ArrayList() for (stop in schedule.stops) { - val stopTime = envelopeWithStops.interpolateTotalTime(stop.position) - stop.duration + val stopTime = envelopeWithStops.interpolateArrivalAt(stop.position) stops.add(ResultStops(stopTime, stop.position, stop.duration)) } @@ -161,7 +161,7 @@ fun run( rawInfra.getPhysicalSignalName( loadedSignalInfra.getPhysicalSignal(pathSignal.signal) ), - envelopeWithStops.interpolateTotalTime(sightOffset.distance.meters), + envelopeWithStops.interpolateArrivalAt(sightOffset.distance.meters), sightOffset.distance.meters, "VL" // TODO: find out the real state ) @@ -368,7 +368,7 @@ fun routingRequirements( val stop = stops[stopIdx] val stopTravelledOffset = pathOffsetBuilder.toTravelledPath(stop.pathOffset) if (stop.onStopSignal && entrySignalOffset <= stopTravelledOffset) { - // stop duration is included in interpolateTotalTime() + // stop duration is included in interpolateDepartureFromClamp() criticalPos = stopTravelledOffset break } @@ -379,7 +379,7 @@ fun routingRequirements( // find last time when the train is at the critical location (including stop duration if at // stop) - return envelope.clampInterpolate(criticalPos) + return envelope.interpolateDepartureFromClamp(criticalPos.distance.meters) } val res = mutableListOf() @@ -408,7 +408,7 @@ fun routingRequirements( assert(routeIndex == 0) continue } - val criticalTime = envelope.clampInterpolate(criticalPos) + val criticalTime = envelope.interpolateDepartureFromClamp(criticalPos.distance.meters) zoneRequirements.add(routingZoneRequirement(rawInfra, zonePath, criticalTime)) } res.add( @@ -482,16 +482,6 @@ private fun findLimitingSignal( return LimitingSignal(lastSignalBlockIndex, lastSignalIndex) } -fun EnvelopeTimeInterpolate.clampInterpolate(position: Offset): Double { - val criticalPos = position.distance.meters - if (criticalPos <= 0.0) return 0.0 - if (criticalPos >= endPos) return totalTime - - // find last time when the train is at the critical location (including stop duration if at - // stop) - return interpolateTotalTime(criticalPos) -} - data class ZoneOccupationChangeEvent( val time: TimeDelta, val offset: Offset, @@ -518,7 +508,7 @@ fun zoneOccupationChangeEvents( if (currentOffset.distance > envelope.endPos.meters) break val entryOffset = Offset.max(Offset.zero(), currentOffset) val entryTime = - envelope.interpolateTotalTimeUS(entryOffset.distance.meters).microseconds + envelope.interpolateArrivalAtUS(entryOffset.distance.meters).microseconds val zone = rawInfra.getNextZone(rawInfra.getZonePathEntry(zonePath))!! zoneOccupationChangeEvents.add( ZoneOccupationChangeEvent(entryTime, entryOffset, zoneCount, true, blockIdx, zone) @@ -531,7 +521,7 @@ fun zoneOccupationChangeEvents( val exitOffset = Offset.max(Offset.zero(), currentOffset + trainLength.meters) if (exitOffset.distance <= envelope.endPos.meters) { val exitTime = - envelope.interpolateTotalTimeUS(exitOffset.distance.meters).microseconds + envelope.interpolateDepartureFromUS(exitOffset.distance.meters).microseconds zoneOccupationChangeEvents.add( ZoneOccupationChangeEvent( exitTime, diff --git a/core/src/main/java/fr/sncf/osrd/standalone_sim/StandaloneSim.java b/core/src/main/java/fr/sncf/osrd/standalone_sim/StandaloneSim.java index cddba25e4d5..8fde8587259 100644 --- a/core/src/main/java/fr/sncf/osrd/standalone_sim/StandaloneSim.java +++ b/core/src/main/java/fr/sncf/osrd/standalone_sim/StandaloneSim.java @@ -172,7 +172,7 @@ public static Optional generateAllowanceFromSchedul double lostTime = 0.; for (var schedulePoint : scheduledPoints) { var rangeEndPos = schedulePoint.pathOffset; - double excessTime = schedulePoint.time - maxEffortEnvelope.interpolateTotalTime(rangeEndPos) - lostTime; + double excessTime = schedulePoint.time - maxEffortEnvelope.interpolateArrivalAt(rangeEndPos) - lostTime; if (excessTime < 0) { // TODO: Raise a warning continue; diff --git a/core/src/main/kotlin/fr/sncf/osrd/standalone_sim/ScheduleMetadataExtractorV2.kt b/core/src/main/kotlin/fr/sncf/osrd/standalone_sim/ScheduleMetadataExtractorV2.kt index 852d7223d2c..760b8c9e575 100644 --- a/core/src/main/kotlin/fr/sncf/osrd/standalone_sim/ScheduleMetadataExtractorV2.kt +++ b/core/src/main/kotlin/fr/sncf/osrd/standalone_sim/ScheduleMetadataExtractorV2.kt @@ -61,7 +61,7 @@ fun runScheduleMetadataExtractor( // Compute stops val stops = ArrayList() for (stop in legacyStops) { - val stopTime = envelopeWithStops.interpolateTotalTime(stop.position) - stop.duration + val stopTime = envelopeWithStops.interpolateArrivalAt(stop.position) stops.add(ResultStops(stopTime, stop.position, stop.duration)) } @@ -106,7 +106,7 @@ fun runScheduleMetadataExtractor( rawInfra.getPhysicalSignalName( loadedSignalInfra.getPhysicalSignal(pathSignal.signal) )!!, - envelopeWithStops.interpolateTotalTime(sightOffset.distance.meters).seconds, + envelopeWithStops.interpolateArrivalAt(sightOffset.distance.meters).seconds, sightOffset, "VL" // TODO: find out the real state ) @@ -194,8 +194,7 @@ fun isScheduledPointsHonored( ): Boolean { for (e in schedule) { if (e.arrival == null) continue - var computed = envelopeStopWrapper.interpolateTotalTime(e.pathOffset.distance.meters) - if (e.stopFor != null) computed -= e.stopFor.seconds + val computed = envelopeStopWrapper.interpolateArrivalAt(e.pathOffset.distance.meters) val expected = e.arrival.seconds // Check that the expected arrival time and the simulated one match ~1s if ((computed - expected).absoluteValue > 1.0) { diff --git a/core/src/main/kotlin/fr/sncf/osrd/standalone_sim/StandaloneSimulation.kt b/core/src/main/kotlin/fr/sncf/osrd/standalone_sim/StandaloneSimulation.kt index 893f231e00f..62989d1ee29 100644 --- a/core/src/main/kotlin/fr/sncf/osrd/standalone_sim/StandaloneSimulation.kt +++ b/core/src/main/kotlin/fr/sncf/osrd/standalone_sim/StandaloneSimulation.kt @@ -202,7 +202,7 @@ fun buildFinalEnvelope( scheduledPoints: List ): Envelope { fun getEnvelopeTimeAt(offset: Offset): Double { - return provisionalEnvelope.interpolateTotalTimeClamp(offset.distance.meters) + return provisionalEnvelope.interpolateDepartureFromClamp(offset.distance.meters) } var prevFixedPointOffset = Offset(0.meters) var prevFixedPointDepartureTime = 0.0 @@ -288,8 +288,8 @@ fun distributeAllowance( envelope: Envelope = provisionalEnvelope ): Double { assert(from < to) - val start = envelope.interpolateTotalTimeClamp(from.distance.meters) - val end = envelope.interpolateTotalTimeClamp(to.distance.meters) + val start = envelope.interpolateDepartureFromClamp(from.distance.meters) + val end = envelope.interpolateDepartureFromClamp(to.distance.meters) return end - start } val rangeEnds = margins.boundaries.filter { it > startOffset && it < endOffset }.toMutableList() diff --git a/core/src/main/kotlin/fr/sncf/osrd/stdcm/graph/PostProcessingSimulation.kt b/core/src/main/kotlin/fr/sncf/osrd/stdcm/graph/PostProcessingSimulation.kt index ac6c1c3d82e..1ed3296eb21 100644 --- a/core/src/main/kotlin/fr/sncf/osrd/stdcm/graph/PostProcessingSimulation.kt +++ b/core/src/main/kotlin/fr/sncf/osrd/stdcm/graph/PostProcessingSimulation.kt @@ -341,8 +341,8 @@ private fun makeAllowanceRanges( val res = ArrayList() for (point in fixedPoints) { val baseTime = - envelope.interpolateTotalTimeClamp(point.offset.distance.meters) - - envelope.interpolateTotalTimeClamp(transition) + envelope.interpolateArrivalAtClamp(point.offset.distance.meters) - + envelope.interpolateDepartureFromClamp(transition) val pointArrivalTime = transitionTime + baseTime val neededDelay = max(0.0, point.time - pointArrivalTime - prevAddedTime) diff --git a/core/src/main/kotlin/fr/sncf/osrd/stdcm/infra_exploration/InfraExplorerWithEnvelope.kt b/core/src/main/kotlin/fr/sncf/osrd/stdcm/infra_exploration/InfraExplorerWithEnvelope.kt index 2941b37cad6..2ef4d95d509 100644 --- a/core/src/main/kotlin/fr/sncf/osrd/stdcm/infra_exploration/InfraExplorerWithEnvelope.kt +++ b/core/src/main/kotlin/fr/sncf/osrd/stdcm/infra_exploration/InfraExplorerWithEnvelope.kt @@ -32,7 +32,7 @@ import fr.sncf.osrd.utils.units.Offset * * Note: the first envelope doesn't necessarily cover the first block in its entirety, while path * offsets start at offset=0 on the first block. There are two ways to properly handle time at path - * offsets: either using `explorer.interpolateTimeClamp`, or by converting the offsets into + * offsets: either using `explorer.interpolateDepartureFromClamp`, or by converting the offsets into * `Offset` using the underlying incremental path. */ interface InfraExplorerWithEnvelope : InfraExplorer { @@ -44,10 +44,10 @@ interface InfraExplorerWithEnvelope : InfraExplorer { fun addEnvelope(envelope: EnvelopeInterpolate): InfraExplorerWithEnvelope /** - * Calls `InterpolateTotalTimeClamp` on the underlying envelope, taking the travelled path + * Calls `InterpolateDepartureFromClamp` on the underlying envelope, taking the travelled path * offset into account. */ - fun interpolateTimeClamp(pathOffset: Offset): Double + fun interpolateDepartureFromClamp(pathOffset: Offset): Double /** Returns the spacing requirements since the last update */ fun getSpacingRequirements(): List diff --git a/core/src/main/kotlin/fr/sncf/osrd/stdcm/infra_exploration/InfraExplorerWithEnvelopeImpl.kt b/core/src/main/kotlin/fr/sncf/osrd/stdcm/infra_exploration/InfraExplorerWithEnvelopeImpl.kt index 8c9ce2bed76..fb1ce238fa4 100644 --- a/core/src/main/kotlin/fr/sncf/osrd/stdcm/infra_exploration/InfraExplorerWithEnvelopeImpl.kt +++ b/core/src/main/kotlin/fr/sncf/osrd/stdcm/infra_exploration/InfraExplorerWithEnvelopeImpl.kt @@ -62,9 +62,9 @@ data class InfraExplorerWithEnvelopeImpl( return this } - override fun interpolateTimeClamp(pathOffset: Offset): Double { + override fun interpolateDepartureFromClamp(pathOffset: Offset): Double { return getFullEnvelope() - .interpolateTotalTimeClamp( + .interpolateDepartureFromClamp( getIncrementalPath().toTravelledPath(pathOffset).distance.meters ) } diff --git a/core/src/main/kotlin/fr/sncf/osrd/stdcm/preprocessing/implementation/BlockAvailability.kt b/core/src/main/kotlin/fr/sncf/osrd/stdcm/preprocessing/implementation/BlockAvailability.kt index d06da2b080b..b77a79d3d34 100644 --- a/core/src/main/kotlin/fr/sncf/osrd/stdcm/preprocessing/implementation/BlockAvailability.kt +++ b/core/src/main/kotlin/fr/sncf/osrd/stdcm/preprocessing/implementation/BlockAvailability.kt @@ -35,8 +35,8 @@ data class BlockAvailability( val spacingRequirements = if (needFullRequirements) infraExplorer.getFullSpacingRequirements() else infraExplorer.getSpacingRequirements() - val pathStartTime = startTime - infraExplorer.interpolateTimeClamp(startOffset) - val endTime = infraExplorer.interpolateTimeClamp(endOffset) + pathStartTime + val pathStartTime = startTime - infraExplorer.interpolateDepartureFromClamp(startOffset) + val endTime = infraExplorer.interpolateDepartureFromClamp(endOffset) + pathStartTime // Modify the spacing requirements to adjust for the start time, // and filter out the ones that are outside the relevant time range @@ -90,7 +90,7 @@ data class BlockAvailability( var value = 0.0 while (i++ < 20 && !search.complete()) { value = search.input - search.feedback(envelope.interpolateTotalTimeClamp(search.input)) + search.feedback(envelope.interpolateDepartureFromClamp(search.input)) } return Offset(Distance.fromMeters(value)) } diff --git a/core/src/test/kotlin/fr/sncf/osrd/standalone_sim/StandaloneSimulationTest.kt b/core/src/test/kotlin/fr/sncf/osrd/standalone_sim/StandaloneSimulationTest.kt index 6da8160f901..5143534226f 100644 --- a/core/src/test/kotlin/fr/sncf/osrd/standalone_sim/StandaloneSimulationTest.kt +++ b/core/src/test/kotlin/fr/sncf/osrd/standalone_sim/StandaloneSimulationTest.kt @@ -110,15 +110,17 @@ class StandaloneSimulationTest { listOf( SimulationScheduleItem( thirdDistance, - maxEffortEnvelope.interpolateTotalTime(thirdDistance.distance.meters).seconds + - 60.seconds, + maxEffortEnvelope + .interpolateDepartureFrom(thirdDistance.distance.meters) + .seconds + 60.seconds, null, false ), SimulationScheduleItem( halfDistance, - maxEffortEnvelope.interpolateTotalTime(halfDistance.distance.meters).seconds + - 120.seconds, + maxEffortEnvelope + .interpolateDepartureFrom(halfDistance.distance.meters) + .seconds + 120.seconds, 15.seconds, true ), diff --git a/core/src/test/kotlin/fr/sncf/osrd/stdcm/DepartureTimeShiftTests.kt b/core/src/test/kotlin/fr/sncf/osrd/stdcm/DepartureTimeShiftTests.kt index ad1a29f295b..1651d913e16 100644 --- a/core/src/test/kotlin/fr/sncf/osrd/stdcm/DepartureTimeShiftTests.kt +++ b/core/src/test/kotlin/fr/sncf/osrd/stdcm/DepartureTimeShiftTests.kt @@ -37,7 +37,7 @@ class DepartureTimeShiftTests { .run()!! val secondBlockEntryTime = (res.departureTime + - res.envelope.interpolateTotalTime(infra.getBlockLength(firstBlock).distance.meters)) + res.envelope.interpolateArrivalAt(infra.getBlockLength(firstBlock).distance.meters)) Assertions.assertTrue(secondBlockEntryTime >= 3600) occupancyTest(res, occupancyGraph) } @@ -70,7 +70,7 @@ class DepartureTimeShiftTests { .run()!! val secondBlockEntryTime = (res.departureTime + - res.envelope.interpolateTotalTime(infra.getBlockLength(firstBlock).distance.meters)) + res.envelope.interpolateArrivalAt(infra.getBlockLength(firstBlock).distance.meters)) Assertions.assertTrue(secondBlockEntryTime >= 3600) occupancyTest(res, occupancyGraph) } diff --git a/core/src/test/kotlin/fr/sncf/osrd/stdcm/STDCMHelpers.kt b/core/src/test/kotlin/fr/sncf/osrd/stdcm/STDCMHelpers.kt index 5d07712727f..19db271ebf8 100644 --- a/core/src/test/kotlin/fr/sncf/osrd/stdcm/STDCMHelpers.kt +++ b/core/src/test/kotlin/fr/sncf/osrd/stdcm/STDCMHelpers.kt @@ -137,12 +137,12 @@ fun occupancyTest( for ((timeStart, timeEnd, distanceStart, distanceEnd) in blockOccupancies) { val enterTime = res.departureTime + - envelopeWrapper.interpolateTotalTimeClamp( + envelopeWrapper.interpolateArrivalAtClamp( (startBlockPosition + distanceStart).meters ) val exitTime = res.departureTime + - envelopeWrapper.interpolateTotalTimeClamp( + envelopeWrapper.interpolateDepartureFromClamp( (startBlockPosition + distanceEnd).meters ) Assertions.assertTrue( diff --git a/core/src/test/kotlin/fr/sncf/osrd/stdcm/StandardAllowanceTests.kt b/core/src/test/kotlin/fr/sncf/osrd/stdcm/StandardAllowanceTests.kt index 538e9a3cb23..45a06e861dc 100644 --- a/core/src/test/kotlin/fr/sncf/osrd/stdcm/StandardAllowanceTests.kt +++ b/core/src/test/kotlin/fr/sncf/osrd/stdcm/StandardAllowanceTests.kt @@ -103,7 +103,7 @@ class StandardAllowanceTests { Assertions.assertNotNull(res.withAllowance!!) val secondBlockEntryTime = (res.withAllowance.departureTime + - res.withAllowance.envelope.interpolateTotalTime( + res.withAllowance.envelope.interpolateArrivalAt( infra.getBlockLength(firstBlock).distance.meters )) Assertions.assertTrue(secondBlockEntryTime >= 3600 - TIME_STEP) @@ -137,7 +137,7 @@ class StandardAllowanceTests { Assertions.assertNotNull(res.withAllowance!!) val timeEnterOccupiedSection = (res.withAllowance.departureTime + - res.withAllowance.envelope.interpolateTotalTime(5000.0)) + res.withAllowance.envelope.interpolateArrivalAt(5000.0)) Assertions.assertEquals(3600.0, timeEnterOccupiedSection, 3 * TIME_STEP) occupancyTest(res.withAllowance, occupancyGraph, 2 * TIME_STEP) checkAllowanceResult(res, allowance) @@ -185,7 +185,7 @@ class StandardAllowanceTests { .setStandardAllowance(allowance) .run()!! occupancyTest(res, occupancyGraph, TIME_STEP) - val thirdBlockEntryTime = (res.departureTime + res.envelope.interpolateTotalTime(11000.0)) + val thirdBlockEntryTime = (res.departureTime + res.envelope.interpolateArrivalAt(11000.0)) Assertions.assertEquals( 1000.0, thirdBlockEntryTime, @@ -234,7 +234,7 @@ class StandardAllowanceTests { .setStandardAllowance(allowance) .run()!! occupancyTest(res, occupancyGraph, TIME_STEP) - val thirdBlockEntryTime = (res.departureTime + res.envelope.interpolateTotalTime(10001.0)) + val thirdBlockEntryTime = (res.departureTime + res.envelope.interpolateArrivalAt(10001.0)) Assertions.assertEquals(1000.0, thirdBlockEntryTime, 3 * TIME_STEP) } @@ -360,7 +360,7 @@ class StandardAllowanceTests { .setStandardAllowance(allowance) .run()!! occupancyTest(res, occupancyGraph, TIME_STEP) - val thirdBlockEntryTime = (res.departureTime + res.envelope.interpolateTotalTime(11000.0)) + val thirdBlockEntryTime = (res.departureTime + res.envelope.interpolateArrivalAt(11000.0)) // 2 allowances + the extra safety TIME_STEP added when avoiding conflicts gives us 3 * // TIME_STEP Assertions.assertEquals(1000.0, thirdBlockEntryTime, 3 * TIME_STEP) diff --git a/core/src/test/kotlin/fr/sncf/osrd/stdcm/infra_exploration/InfraExplorerWithEnvelopeTests.kt b/core/src/test/kotlin/fr/sncf/osrd/stdcm/infra_exploration/InfraExplorerWithEnvelopeTests.kt index 16c7e12b119..3dfd83fab25 100644 --- a/core/src/test/kotlin/fr/sncf/osrd/stdcm/infra_exploration/InfraExplorerWithEnvelopeTests.kt +++ b/core/src/test/kotlin/fr/sncf/osrd/stdcm/infra_exploration/InfraExplorerWithEnvelopeTests.kt @@ -200,7 +200,7 @@ class InfraExplorerWithEnvelopeTests { /** * Test that the envelope bounds are equal, then iterate by an arbitrary step length to test for - * interpolateTotalTime equality. + * interpolateDepartureFrom/ArrivalAt equality (respectively with/without stop duration). */ private fun testEnvelopeTimeEquality(expected: Envelope, actual: EnvelopeTimeInterpolate) { assertTrue { arePositionsEqual(expected.beginPos, actual.beginPos) } @@ -209,8 +209,14 @@ class InfraExplorerWithEnvelopeTests { while (position < expected.endPos) { assertTrue( areTimesEqual( - expected.interpolateTotalTime(position), - actual.interpolateTotalTime(position), + expected.interpolateDepartureFrom(position), + actual.interpolateDepartureFrom(position), + ) + ) + assertTrue( + areTimesEqual( + expected.interpolateArrivalAt(position), + actual.interpolateArrivalAt(position), ) ) position += 1.0 diff --git a/core/src/test/kotlin/fr/sncf/osrd/stdcm/preprocessing/DummyBlockAvailability.kt b/core/src/test/kotlin/fr/sncf/osrd/stdcm/preprocessing/DummyBlockAvailability.kt index 0dc8443fa22..b73458df0be 100644 --- a/core/src/test/kotlin/fr/sncf/osrd/stdcm/preprocessing/DummyBlockAvailability.kt +++ b/core/src/test/kotlin/fr/sncf/osrd/stdcm/preprocessing/DummyBlockAvailability.kt @@ -117,7 +117,7 @@ class DummyBlockAvailability( ): BlockAvailabilityInterface.Availability { val resourceUses = generateResourcesForPath(infraExplorer, startOffset, endOffset) // startTime refers to the time at startOffset, we need to offset it - val pathStartTime = startTime - infraExplorer.interpolateTimeClamp(startOffset) + val pathStartTime = startTime - infraExplorer.interpolateDepartureFromClamp(startOffset) val unavailability = findMinimumDelay(infraExplorer, resourceUses, pathStartTime, startOffset) return unavailability ?: findMaximumDelay(infraExplorer, resourceUses, pathStartTime) @@ -244,8 +244,8 @@ class DummyBlockAvailability( if (startOffset.distance > blockExitOffset || endOffset.distance < blockEnterOffset) return null - val enterTime = explorer.interpolateTimeClamp(Offset(blockEnterOffset)) - val exitTime = explorer.interpolateTimeClamp(Offset(blockExitOffset)) + val enterTime = explorer.interpolateDepartureFromClamp(Offset(blockEnterOffset)) + val exitTime = explorer.interpolateDepartureFromClamp(Offset(blockExitOffset)) return TimeInterval(enterTime, exitTime) }