From 4dc1a17d7b6bf4f7a47eea4147736532602d995f Mon Sep 17 00:00:00 2001 From: Erashin Date: Fri, 10 Jan 2025 11:24:50 +0100 Subject: [PATCH 1/2] core: add etcs loa logic to etcs braking simulator Signed-off-by: Erashin --- .../sncf/osrd/envelope_sim/etcs/Constants.kt | 65 +++- .../envelope_sim/etcs/ETCSBrakingCurves.kt | 367 ++++++++++++++---- .../envelope_sim/etcs/ETCSBrakingSimulator.kt | 3 +- 3 files changed, 360 insertions(+), 75 deletions(-) diff --git a/core/envelope-sim/src/main/kotlin/fr/sncf/osrd/envelope_sim/etcs/Constants.kt b/core/envelope-sim/src/main/kotlin/fr/sncf/osrd/envelope_sim/etcs/Constants.kt index c6e4fbfc97b..43faf4b6ca5 100644 --- a/core/envelope-sim/src/main/kotlin/fr/sncf/osrd/envelope_sim/etcs/Constants.kt +++ b/core/envelope-sim/src/main/kotlin/fr/sncf/osrd/envelope_sim/etcs/Constants.kt @@ -1,6 +1,69 @@ package fr.sncf.osrd.envelope_sim.etcs -/** See Subset referenced in ETCSBrakingSimulator: table in Appendix A.3.1. */ +/** + * Formulas are found in `SUBSET-026-3v400.pdf` from the file at + * https://www.era.europa.eu/system/files/2023-09/index004_-_SUBSET-026_v400.zip and in + * `SUBSET-041_v400.pdf` from the file at + * https://www.era.europa.eu/system/files/2023-09/index014_-_SUBSET-041_v400.pdf + */ + +/** + * National Default Value: permission to inhibit the compensation of the speed measurement accuracy. + * See Subset 026: table in Appendix A.3.2. + */ +const val qNvinhsmicperm = false + +/** + * Estimated acceleration during tBerem, worst case scenario (aEst2 is between 0 and 0.4), expressed + * in m/s². See Subset 026: §3.13.9.3.2.9. + */ +const val aEst2 = 0.4 + +/** See Subset 026: table in Appendix A.3.1. */ +const val dvEbiMin = 7.5 / 3.6 // m/s +const val dvEbiMax = 15.0 / 3.6 // m/s +const val vEbiMin = 110.0 / 3.6 // m/s +const val vEbiMax = 210.0 / 3.6 // m/s +const val tWarning = 2.0 // s const val tDriver = 4.0 // s const val mRotatingMax = 15.0 // % const val mRotatingMin = 2.0 // % + +/** See Subset 041: §5.3.1.2. */ +const val vUraMinLimit = 30 / 3.6 // m/s +const val vUraMaxLimit = 500 / 3.6 // m/s +const val vUraMin = 2 / 3.6 // m/s +const val vUraMax = 12 / 3.6 // m/s + +/** See Subset 041: §5.3.1.2. */ +fun vUra(speed: Double): Double { + return interpolateLinearSpeed(speed, vUraMinLimit, vUraMaxLimit, vUraMin, vUraMax) +} + +/** See Subset 026: §3.13.9.3.2.10. */ +fun vDelta0(speed: Double): Double { + return if (!qNvinhsmicperm) vUra(speed) else 0.0 +} + +/** See Subset 026: §3.13.9.2.3. */ +fun dvEbi(speed: Double): Double { + return interpolateLinearSpeed(speed, vEbiMin, vEbiMax, dvEbiMin, dvEbiMax) +} + +/** + * The linear curve is the following: below minSpeedLimit = minSpeed, above maxSpeedLimit = + * maxSpeed, in between is a linear curve. This method takes a speed input and converts it + * accordingly. + */ +private fun interpolateLinearSpeed( + speed: Double, + minSpeedLimit: Double, + maxSpeedLimit: Double, + minSpeed: Double, + maxSpeed: Double +): Double { + return if (speed <= minSpeedLimit) minSpeed + else if (speed < maxSpeedLimit) + (maxSpeed - minSpeed) / (maxSpeedLimit - minSpeedLimit) * (speed - minSpeedLimit) + minSpeed + else maxSpeed +} diff --git a/core/envelope-sim/src/main/kotlin/fr/sncf/osrd/envelope_sim/etcs/ETCSBrakingCurves.kt b/core/envelope-sim/src/main/kotlin/fr/sncf/osrd/envelope_sim/etcs/ETCSBrakingCurves.kt index 60fabadc724..a1e931d4c2c 100644 --- a/core/envelope-sim/src/main/kotlin/fr/sncf/osrd/envelope_sim/etcs/ETCSBrakingCurves.kt +++ b/core/envelope-sim/src/main/kotlin/fr/sncf/osrd/envelope_sim/etcs/ETCSBrakingCurves.kt @@ -14,6 +14,10 @@ import fr.sncf.osrd.envelope_sim.overlays.EnvelopeDeceleration import kotlin.math.max import kotlin.math.min +/** + * Formulas are found in `SUBSET-026-3v400.pdf` from the file at + * https://www.era.europa.eu/system/files/2023-09/index004_-_SUBSET-026_v400.zip + */ enum class BrakingCurveType { EBD, // Emergency Brake Deceleration EBI, // Emergency Brake Intervention @@ -38,69 +42,191 @@ fun addBrakingCurvesAtEOAs( endsOfAuthority: Collection ): Envelope { val sortedEndsOfAuthority = endsOfAuthority.sortedBy { it.offsetEOA } - var beginPos = 0.0 + var beginPos = envelope.beginPos val builder = OverlayEnvelopeBuilder.forward(envelope) for (endOfAuthority in sortedEndsOfAuthority) { val targetPosition = endOfAuthority.offsetEOA.distance.meters val targetSpeed = 0.0 + val maxSpeedEnvelope = envelope.maxSpeed val overhead = Envelope.make( EnvelopePart.generateTimes( listOf(EnvelopeProfile.CONSTANT_SPEED), - doubleArrayOf(beginPos, targetPosition), - doubleArrayOf(envelope.maxSpeed, envelope.maxSpeed) + doubleArrayOf(0.0, targetPosition), + doubleArrayOf(maxSpeedEnvelope, maxSpeedEnvelope) ) ) + val sbdCurve = + computeBrakingCurve( + context, + overhead, + targetPosition, + targetSpeed, + BrakingType.ETCS_SBD + ) + assert( + sbdCurve.beginPos >= 0 && + sbdCurve.endSpeed == targetSpeed && + sbdCurve.endPos == targetPosition + ) val guiCurve = computeBrakingCurve( context, overhead, - beginPos, targetPosition, targetSpeed, BrakingType.ETCS_GUI ) - val sbdCurve = + assert( + guiCurve.beginPos >= 0.0 && + (guiCurve.beginSpeed == maxSpeedEnvelope || guiCurve.beginPos == 0.0) && + guiCurve.endSpeed == targetSpeed && + guiCurve.endPos == targetPosition + ) + + val fullIndicationCurve = + computeIndicationBrakingCurveFromRef(context, sbdCurve, BrakingCurveType.SBD, guiCurve) + assert( + fullIndicationCurve.endSpeed == targetSpeed && + fullIndicationCurve.endPos == targetPosition + ) + + // Indication curve for EOAs will always have at least 2 points with positive positions. + // This method cannot return a null. + val indicationCurve = + keepBrakingCurveUnderOverlay(fullIndicationCurve, envelope, beginPos)!! + assert( + indicationCurve.beginPos >= beginPos && + indicationCurve.beginSpeed <= maxSpeedEnvelope && + indicationCurve.endSpeed == targetSpeed && + indicationCurve.endPos == targetPosition + ) + builder.addPart(indicationCurve) + + // We build EOAs along the path. We need to handle overlaps with the next EOA. To do so, we + // shift the left position constraint, beginPos, to this EOA's target position. + beginPos = targetPosition + } + return builder.build() +} + +/** Compute braking curves at every limit of authority. */ +fun addBrakingCurvesAtLOAs( + envelope: Envelope, + context: EnvelopeSimContext, + limitsOfAuthority: Collection +): Envelope { + val sortedLimitsOfAuthority = limitsOfAuthority.sortedBy { it.offset } + val beginPos = envelope.beginPos + var envelopeWithLoaBrakingCurves = envelope + var builder = OverlayEnvelopeBuilder.forward(envelopeWithLoaBrakingCurves) + for (limitOfAuthority in sortedLimitsOfAuthority) { + val targetPosition = limitOfAuthority.offset.distance.meters + val targetSpeed = limitOfAuthority.speed + val maxSpeedEnvelope = envelopeWithLoaBrakingCurves.maxSpeed + + // Add maxBecDeltaSpeed to EBD curve overlay so it reaches a sufficiently high speed to + // guarantee that, after the speed translation, the corresponding EBI curve does intersect + // with envelope max speed. + val maxBecDeltaSpeed = maxBecDeltaSpeed() + val maxSpeedEbd = maxSpeedEnvelope + maxBecDeltaSpeed + val overhead = + Envelope.make( + EnvelopePart.generateTimes( + listOf(EnvelopeProfile.CONSTANT_SPEED), + doubleArrayOf(0.0, targetPosition), + doubleArrayOf(maxSpeedEbd, maxSpeedEbd) + ) + ) + + val ebdCurve = computeBrakingCurve( context, overhead, - beginPos, targetPosition, targetSpeed, - BrakingType.ETCS_SBD + BrakingType.ETCS_EBD ) - val fullIndicationCurve = - computeIndicationBrakingCurveFromRef( + assert(ebdCurve.beginPos >= 0.0 && ebdCurve.endPos >= targetPosition) + val guiCurve = + computeBrakingCurve( context, - sbdCurve, - BrakingCurveType.SBD, - guiCurve, + overhead, + targetPosition, + targetSpeed, + BrakingType.ETCS_GUI + ) + assert( + guiCurve.beginPos >= 0.0 && + (guiCurve.beginSpeed == maxSpeedEbd || guiCurve.beginPos == 0.0) && + guiCurve.endPos == targetPosition + ) + + val ebiCurve = computeEbiBrakingCurveFromEbd(context, ebdCurve, targetSpeed) + assert(ebiCurve.endSpeed == targetSpeed) + + val fullIndicationCurve = + computeIndicationBrakingCurveFromRef(context, ebiCurve, BrakingCurveType.EBI, guiCurve) + assert( + fullIndicationCurve.endSpeed == targetSpeed && + fullIndicationCurve.endPos <= targetPosition + ) + + val indicationCurve = + keepBrakingCurveUnderOverlay( + fullIndicationCurve, + envelopeWithLoaBrakingCurves, beginPos ) - val indicationCurve = keepBrakingCurveUnderOverlay(fullIndicationCurve, envelope, beginPos) - builder.addPart(indicationCurve) - beginPos = targetPosition + + if (indicationCurve != null) { + assert( + indicationCurve.beginSpeed <= maxSpeedEnvelope && + indicationCurve.endSpeed == targetSpeed && + indicationCurve.endPos <= targetPosition + ) + builder.addPart(indicationCurve) + } + + val endOfIndicationCurve = indicationCurve?.endPos ?: beginPos + if (endOfIndicationCurve < targetPosition) { + // Maintain target speed until target position, i.e. LOA. + val maintainTargetSpeedCurve = + EnvelopePart.generateTimes( + listOf(EnvelopeProfile.CONSTANT_SPEED), + doubleArrayOf(endOfIndicationCurve, targetPosition), + doubleArrayOf(targetSpeed, targetSpeed) + ) + builder.addPart(maintainTargetSpeedCurve) + } + + // We build the LOAs along the path, and they don't all have the same target speeds. To + // handle intersections with the next LOA, it is needed to add this LOA braking curve to the + // overlay builder that will be used to compute the following LOAs. + envelopeWithLoaBrakingCurves = builder.build() + builder = OverlayEnvelopeBuilder.forward(envelopeWithLoaBrakingCurves) } - return builder.build() + return envelopeWithLoaBrakingCurves } /** Compute braking curve: used to compute EBD, SBD or GUI. */ private fun computeBrakingCurve( context: EnvelopeSimContext, envelope: Envelope, - beginPos: Double, targetPosition: Double, targetSpeed: Double, brakingType: BrakingType ): EnvelopePart { - // If the stopPosition is below begin position, the input is invalid + assert( + brakingType == BrakingType.ETCS_EBD || + brakingType == BrakingType.ETCS_SBD || + brakingType == BrakingType.ETCS_GUI + ) // If the stopPosition is after the end of the path, the input is invalid except if it is an - // SVL, i.e. the target speed is 0 and the curve to compute is an EBD + // SVL, i.e. the target speed is 0 and the curve to compute is an EBD. if ( - targetPosition <= beginPos || - (targetPosition > context.path.length && - targetSpeed == 0.0 && - brakingType != BrakingType.ETCS_EBD) + (targetPosition > context.path.length && + !(targetSpeed == 0.0 && brakingType == BrakingType.ETCS_EBD)) ) throw RuntimeException( String.format( @@ -113,8 +239,8 @@ private fun computeBrakingCurve( val overlayBuilder = ConstrainedEnvelopePartBuilder( partBuilder, - PositionConstraint(beginPos, targetPosition), - SpeedConstraint(0.0, EnvelopePartConstraintType.FLOOR), + PositionConstraint(0.0, targetPosition), + SpeedConstraint(targetSpeed, EnvelopePartConstraintType.FLOOR), EnvelopeConstraint(envelope, EnvelopePartConstraintType.CEILING) ) EnvelopeDeceleration.decelerate( @@ -125,22 +251,73 @@ private fun computeBrakingCurve( -1.0, brakingType ) - val brakingCurve = partBuilder.build() + var brakingCurve = partBuilder.build() + + if (brakingType == BrakingType.ETCS_EBD && targetSpeed != 0.0) { + // TODO: by doing this, there is an approximation on the gradient used. TBD at a later date. + // When target is an LOA, EBD reaches target position at target speed + dVEbi: shift + // envelope to make it so. See Subset 026: §3.13.8.3.1, figure 40. + val dvEbi = dvEbi(targetSpeed) + val vTargetPosition = targetSpeed + dvEbi + // Simplification: if EBD does not reach this limit, do not shift it. Impacts few very + // specific cases (LOA/SVL close to 0.0, with an acceleration curve which would hit them + // differently if shifted). Manage later on if needed. + if ( + vTargetPosition <= brakingCurve.beginSpeed && vTargetPosition >= brakingCurve.endSpeed + ) { + val intersection = brakingCurve.interpolatePosition(targetSpeed + dvEbi) + brakingCurve = + brakingCurve.copyAndShift( + targetPosition - intersection, + 0.0, + Double.POSITIVE_INFINITY + ) + } + } + return brakingCurve } /** - * Compute Indication curve: EBI/SBD -> SBI -> PS -> IND. See Subset referenced in - * ETCSBrakingSimulator: figures 45 and 46. + * Compute EBI curve from EBD curve. Resulting EBI stops at target speed. See Subset 026: figure 45. */ +private fun computeEbiBrakingCurveFromEbd( + context: EnvelopeSimContext, + ebdCurve: EnvelopePart, + targetSpeed: Double +): EnvelopePart { + val pointCount = ebdCurve.pointCount() + val newPositions = DoubleArray(pointCount) + val newSpeeds = DoubleArray(pointCount) + for (i in 0 until ebdCurve.pointCount()) { + val speed = ebdCurve.getPointSpeed(i) + val becParams = computeBecParams(context, ebdCurve, speed, targetSpeed) + val newPos = ebdCurve.getPointPos(i) - becParams.dBec + val newSpeed = speed - becParams.deltaBecSpeed + newPositions[i] = newPos + newSpeeds[i] = newSpeed + } + + val fullBrakingCurve = + EnvelopePart.generateTimes(listOf(EnvelopeProfile.BRAKING), newPositions, newSpeeds) + + // Make EBI stop at target speed. + val intersection = fullBrakingCurve.interpolatePosition(targetSpeed) + return fullBrakingCurve.sliceWithSpeeds( + fullBrakingCurve.beginPos, + fullBrakingCurve.beginSpeed, + intersection, + targetSpeed + ) +} + +/** Compute Indication curve: EBI/SBD -> SBI -> PS -> IND. See Subset 026: figures 45 and 46. */ private fun computeIndicationBrakingCurveFromRef( context: EnvelopeSimContext, refBrakingCurve: EnvelopePart, refBrakingCurveType: BrakingCurveType, - guiCurve: EnvelopePart, - beginPos: Double + guiCurve: EnvelopePart ): EnvelopePart { - assert(refBrakingCurve.endPos > beginPos) val rollingStock = context.rollingStock val tBs = when (refBrakingCurveType) { @@ -152,60 +329,39 @@ private fun computeIndicationBrakingCurveFromRef( ) } - val reversedNewPos = ArrayList() - val reversedNewSpeeds = ArrayList() - for (i in refBrakingCurve.pointCount() - 1 downTo 0) { - val reversedNewPosIndex = refBrakingCurve.pointCount() - 1 - i + val pointCount = refBrakingCurve.pointCount() + val newPositions = DoubleArray(pointCount) + val newSpeeds = DoubleArray(pointCount) + for (i in 0 until refBrakingCurve.pointCount()) { val speed = refBrakingCurve.getPointSpeed(i) val sbiPosition = getSbiPosition(refBrakingCurve.getPointPos(i), speed, tBs) val permittedSpeedPosition = getPermittedSpeedPosition(sbiPosition, speed) val adjustedPermittedSpeedPosition = - getAdjustedPermittedSpeedPosition( - permittedSpeedPosition, - guiCurve.interpolatePosition(speed) - ) + getAdjustedPermittedSpeedPosition(permittedSpeedPosition, speed, guiCurve) val indicationPosition = getIndicationPosition(adjustedPermittedSpeedPosition, speed, tBs) - if (indicationPosition >= beginPos) { - reversedNewPos.add(indicationPosition) - reversedNewSpeeds.add(speed) - } else { - assert(reversedNewPosIndex > 0 && reversedNewPos[reversedNewPosIndex - 1] > beginPos) - // Interpolate to begin position if reaching a position before it - val prevPos = reversedNewPos[reversedNewPosIndex - 1] - val prevSpeed = reversedNewSpeeds[reversedNewPosIndex - 1] - val speedAtBeginPos = - prevSpeed + - (beginPos - prevPos) * (speed - prevSpeed) / (indicationPosition - prevPos) - reversedNewPos.add(beginPos) - reversedNewSpeeds.add(speedAtBeginPos) - break - } + newPositions[i] = indicationPosition + newSpeeds[i] = speed } - val nbPoints = reversedNewPos.size - val newPosArray = DoubleArray(nbPoints) - val newSpeedsArray = DoubleArray(nbPoints) - for (i in newPosArray.indices) { - newPosArray[i] = reversedNewPos[nbPoints - 1 - i] - newSpeedsArray[i] = reversedNewSpeeds[nbPoints - 1 - i] - } val brakingCurve = - EnvelopePart.generateTimes(listOf(EnvelopeProfile.BRAKING), newPosArray, newSpeedsArray) + EnvelopePart.generateTimes(listOf(EnvelopeProfile.BRAKING), newPositions, newSpeeds) return brakingCurve } /** * Keep the part of the full braking curve which is located underneath the overlay and intersects - * with it or with begin position. + * with it or with begin position. If the part has no intersection, return null. Ex: the part only + * has negative positions, and the overlay starts at 0.0. */ private fun keepBrakingCurveUnderOverlay( fullBrakingCurve: EnvelopePart, overlay: Envelope, beginPos: Double -): EnvelopePart { - assert(fullBrakingCurve.maxSpeed >= overlay.minSpeed) - assert(fullBrakingCurve.endPos > beginPos) +): EnvelopePart? { + if (fullBrakingCurve.endPos <= beginPos) { + return null + } val positions = fullBrakingCurve.clonePositions() val speeds = fullBrakingCurve.cloneSpeeds() val timeDeltas = fullBrakingCurve.cloneTimes() @@ -216,35 +372,102 @@ private fun keepBrakingCurveUnderOverlay( ConstrainedEnvelopePartBuilder( partBuilder, PositionConstraint(beginPos, overlay.endPos), - SpeedConstraint(0.0, EnvelopePartConstraintType.FLOOR), EnvelopeConstraint(overlay, EnvelopePartConstraintType.CEILING) ) overlayBuilder.initEnvelopePart(positions[nbPoints - 1], speeds[nbPoints - 1], -1.0) for (i in fullBrakingCurve.pointCount() - 2 downTo 0) { if (!overlayBuilder.addStep(positions[i], speeds[i], timeDeltas[i])) break } + if (partBuilder.stepCount() <= 1) return null return partBuilder.build() } -/** See Subset referenced in ETCSBrakingSimulator: §3.13.9.3.3.1 and §3.13.9.3.3.2. */ +private data class BecParams(val dBec: Double, val vBec: Double, val speed: Double) { + val deltaBecSpeed: Double = vBec - speed +} + +/** + * Compute the position and speed offsets between EBD and EBI curves, for a given speed. See Subset + * 026: 3.13.9.3.2. + */ +private fun computeBecParams( + context: EnvelopeSimContext, + ebd: EnvelopePart, + speed: Double, + targetSpeed: Double +): BecParams { + val position = ebd.interpolatePosition(speed) + val rollingStock = context.rollingStock + + val vDelta0 = vDelta0(speed) + + val minGrade = TrainPhysicsIntegrator.getMinGrade(rollingStock, context.path, position) + val weightForce = TrainPhysicsIntegrator.getWeightForce(rollingStock, minGrade) + // The time during which the traction effort is still present. See Subset: §3.13.9.3.2.3. + val tTraction = + max( + rollingStock.rjsEtcsBrakeParams.tTractionCutOff - + (tWarning + rollingStock.rjsEtcsBrakeParams.tBs2), + 0.0 + ) + // Estimated acceleration during tTraction, worst case scenario (the train accelerates as much + // as possible). + val aEst1 = + TrainPhysicsIntegrator.computeAcceleration( + rollingStock, + rollingStock.getRollingResistance(speed), + weightForce, + speed, + PhysicsRollingStock.getMaxEffort(speed, context.tractiveEffortCurveMap.get(position)), + 1.0 + ) + // Speed correction due to the traction staying active during tTraction. See Subset: + // §3.13.9.3.2.10. + val vDelta1 = aEst1 * tTraction + + // The remaining time during which the traction effort is not present. See Subset: + // §3.13.9.3.2.6. + val tBerem = max(rollingStock.rjsEtcsBrakeParams.tBe - tTraction, 0.0) + // Speed correction due to the braking system not being active yet. See Subset: §3.13.9.3.2.10. + val vDelta2 = aEst2 * tBerem + + // Compute dBec and vBec. See Subset: §3.13.9.3.2.10. + val maxV = max(speed + vDelta0 + vDelta1, targetSpeed) + val dBec = + max(speed + vDelta0 + vDelta1 / 2, targetSpeed) * tTraction + (maxV + vDelta1 / 2) * tBerem + val vBec = maxV + vDelta2 + + return BecParams(dBec, vBec, speed) +} + +private fun maxBecDeltaSpeed(): Double { + // TODO: correctly compute maxBecDeltaSpeed. TBD at a later date. + return 50.0 / 3.6 +} + +/** See Subset 026: §3.13.9.3.3.1 and §3.13.9.3.3.2. */ private fun getSbiPosition(ebiOrSbdPosition: Double, speed: Double, tbs: Double): Double { return getPreviousPosition(ebiOrSbdPosition, speed, tbs) } -/** See Subset referenced in ETCSBrakingSimulator: §3.13.9.3.5.1. */ +/** See Subset 026: §3.13.9.3.5.1. */ private fun getPermittedSpeedPosition(sbiPosition: Double, speed: Double): Double { return getPreviousPosition(sbiPosition, speed, tDriver) } -/** See Subset referenced in ETCSBrakingSimulator: §3.13.9.3.5.4. */ +/** See Subset 026: §3.13.9.3.5.4. */ private fun getAdjustedPermittedSpeedPosition( permittedSpeedPosition: Double, - guiPosition: Double = Double.POSITIVE_INFINITY + speed: Double, + guiCurve: EnvelopePart ): Double { + val guiPosition = + if (speed > guiCurve.maxSpeed || speed < guiCurve.minSpeed) Double.POSITIVE_INFINITY + else guiCurve.interpolatePosition(speed) return min(permittedSpeedPosition, guiPosition) } -/** See Subset referenced in ETCSBrakingSimulator: §3.13.9.3.6.1 and §3.13.9.3.6.2. */ +/** See Subset 026: §3.13.9.3.6.1 and §3.13.9.3.6.2. */ private fun getIndicationPosition( permittedSpeedPosition: Double, speed: Double, diff --git a/core/envelope-sim/src/main/kotlin/fr/sncf/osrd/envelope_sim/etcs/ETCSBrakingSimulator.kt b/core/envelope-sim/src/main/kotlin/fr/sncf/osrd/envelope_sim/etcs/ETCSBrakingSimulator.kt index e73ed9d69a7..896df43629f 100644 --- a/core/envelope-sim/src/main/kotlin/fr/sncf/osrd/envelope_sim/etcs/ETCSBrakingSimulator.kt +++ b/core/envelope-sim/src/main/kotlin/fr/sncf/osrd/envelope_sim/etcs/ETCSBrakingSimulator.kt @@ -51,8 +51,7 @@ class ETCSBrakingSimulatorImpl(override val context: EnvelopeSimContext) : ETCSB limitsOfAuthority: Collection ): Envelope { if (limitsOfAuthority.isEmpty()) return envelope - // TODO: implement braking at LOAs CORRECTLY - return envelope + return addBrakingCurvesAtLOAs(envelope, context, limitsOfAuthority) } override fun addStopBrakingCurves( From e93a30e90263897f333dafc465c95545f7807667 Mon Sep 17 00:00:00 2001 From: Erashin Date: Thu, 16 Jan 2025 16:35:45 +0100 Subject: [PATCH 2/2] core: remove etcs logic from const braking curves logic in max speed envelope Signed-off-by: Erashin --- .../fr/sncf/osrd/envelope_sim/pipelines/MaxSpeedEnvelope.kt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/core/envelope-sim/src/main/kotlin/fr/sncf/osrd/envelope_sim/pipelines/MaxSpeedEnvelope.kt b/core/envelope-sim/src/main/kotlin/fr/sncf/osrd/envelope_sim/pipelines/MaxSpeedEnvelope.kt index 6c76f2d7378..365691832db 100644 --- a/core/envelope-sim/src/main/kotlin/fr/sncf/osrd/envelope_sim/pipelines/MaxSpeedEnvelope.kt +++ b/core/envelope-sim/src/main/kotlin/fr/sncf/osrd/envelope_sim/pipelines/MaxSpeedEnvelope.kt @@ -135,19 +135,17 @@ object MaxSpeedEnvelope { var envelope = curveWithDecelerations val stops = makeSimStops(context, stopPositions, envelope) envelope = addETCSStopBrakingCurves(etcsSimulator, context, envelope, stops) - envelope = addConstStopBrakingCurves(etcsSimulator, context, envelope, stops) + envelope = addConstStopBrakingCurves(context, envelope, stops) return envelope } /** Generate braking curves overlay at every stop position */ private fun addConstStopBrakingCurves( - etcsSimulator: ETCSBrakingSimulator, context: EnvelopeSimContext, curveWithDecelerations: Envelope, stops: List, ): Envelope { var envelope = curveWithDecelerations - envelope = addETCSStopBrakingCurves(etcsSimulator, context, envelope, stops) for (stop in stops) { if (stop.isETCS) continue // Already handled val partBuilder = EnvelopePartBuilder()