Skip to content

Commit e3dbe47

Browse files
committed
fixup! core: add etcs svl logic
1 parent fb9ad97 commit e3dbe47

File tree

3 files changed

+28
-18
lines changed

3 files changed

+28
-18
lines changed

core/envelope-sim/src/main/kotlin/fr/sncf/osrd/envelope_sim/etcs/Constants.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ const val qNvinhsmicperm = false
1717
* National Default Value: permission to follow release speed at 40km/h near the EoA. See Subset
1818
* 026: table in Appendix A.3.2.
1919
*/
20-
const val releaseSpeed = 40.0 / 3.6 // m/s
20+
const val nationalReleaseSpeed = 40.0 / 3.6 // m/s
2121

2222
/**
2323
* Estimated acceleration during tBerem, worst case scenario (aEst2 is between 0 and 0.4), expressed

core/envelope-sim/src/main/kotlin/fr/sncf/osrd/envelope_sim/etcs/ETCSBrakingCurves.kt

+24-15
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ fun addBrakingCurvesAtEOAs(
8080
)
8181
assert(
8282
indicationCurveSvl.beginSpeed <= maxSpeedEnvelope &&
83-
indicationCurveSvl.endSpeed >= releaseSpeed
83+
indicationCurveSvl.endSpeed >= nationalReleaseSpeed
8484
)
8585
builder.addPart(indicationCurveSvl)
8686
envelopeWithEoaBrakingCurves = builder.build()
@@ -252,8 +252,8 @@ private fun computeSvlIndicationCurve(
252252
if (fullIndicationCurveSvl.beginPos >= targetPosition) return null
253253
val fullIndCurveSvlWithMaintain: Envelope
254254
val releaseSpeedPosition =
255-
if (fullIndicationCurveSvl.maxSpeed >= releaseSpeed)
256-
fullIndicationCurveSvl.interpolatePosition(releaseSpeed)
255+
if (fullIndicationCurveSvl.maxSpeed >= nationalReleaseSpeed)
256+
fullIndicationCurveSvl.interpolatePosition(nationalReleaseSpeed)
257257
else fullIndicationCurveSvl.beginPos
258258
if (releaseSpeedPosition >= targetPosition) {
259259
fullIndCurveSvlWithMaintain =
@@ -265,7 +265,7 @@ private fun computeSvlIndicationCurve(
265265
EnvelopePart.generateTimes(
266266
listOf(EnvelopeProfile.CONSTANT_SPEED),
267267
doubleArrayOf(releaseSpeedPosition, targetPosition),
268-
doubleArrayOf(releaseSpeed, releaseSpeed)
268+
doubleArrayOf(nationalReleaseSpeed, nationalReleaseSpeed)
269269
)
270270
fullIndCurveSvlWithMaintain =
271271
if (releaseSpeedPosition == fullIndicationCurveSvl.beginPos) {
@@ -276,7 +276,7 @@ private fun computeSvlIndicationCurve(
276276
fullIndicationCurveSvl.beginPos,
277277
fullIndicationCurveSvl.beginSpeed,
278278
releaseSpeedPosition,
279-
releaseSpeed
279+
nationalReleaseSpeed
280280
),
281281
maintainReleaseSpeedCurve
282282
)
@@ -477,7 +477,6 @@ private fun keepBrakingCurveUnderOverlay(
477477
mergedArray + currentArray.drop(1).toDoubleArray()
478478
}
479479
val timeDeltas = fullBrakingCurve.flatMap { it.cloneTimes().asList() }
480-
val nbPoints = positions.size
481480

482481
val partBuilder = EnvelopePartBuilder()
483482
partBuilder.setAttr(EnvelopeProfile.BRAKING)
@@ -487,23 +486,33 @@ private fun keepBrakingCurveUnderOverlay(
487486
PositionConstraint(max(beginPos, fullBrakingCurve.beginPos), overlay.endPos),
488487
EnvelopeConstraint(overlay, EnvelopePartConstraintType.CEILING)
489488
)
490-
// Find the last point of the braking curve which is located below the overlay envelope, and
491-
// init the overlay builder there.
492-
var lastIndex = nbPoints - 1
489+
val lastIndex = getIndexOfLastPointBeneathOverlay(positions, speeds, overlay)
490+
// To create a braking curve with overlay builder, we need at least 2 positions.
491+
if (lastIndex <= 0) return null
492+
overlayBuilder.initEnvelopePart(positions[lastIndex], speeds[lastIndex], -1.0)
493+
for (i in lastIndex - 1 downTo 0) {
494+
if (!overlayBuilder.addStep(positions[i], speeds[i], timeDeltas[i])) break
495+
}
496+
return partBuilder.build()
497+
}
498+
499+
/** Find the index of the last point which is located beneath the overlay envelope, -1 if none exist. */
500+
private fun getIndexOfLastPointBeneathOverlay(
501+
positions: DoubleArray,
502+
speeds: DoubleArray,
503+
overlay: Envelope
504+
): Int {
505+
var lastIndex = positions.size - 1
493506
while (
507+
lastIndex >= 0 &&
494508
speeds[lastIndex] >
495509
overlay
496510
.get(overlay.findRightDir(positions[lastIndex], -1.0))
497511
.interpolateSpeed(positions[lastIndex])
498512
) {
499513
lastIndex--
500-
if (lastIndex == 0) return null
501-
}
502-
overlayBuilder.initEnvelopePart(positions[lastIndex], speeds[lastIndex], -1.0)
503-
for (i in lastIndex - 1 downTo 0) {
504-
if (!overlayBuilder.addStep(positions[i], speeds[i], timeDeltas[i])) break
505514
}
506-
return partBuilder.build()
515+
return lastIndex
507516
}
508517

509518
private data class BecParams(val dBec: Double, val vBec: Double, val speed: Double) {

tests/tests/test_train_schedule.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ def kph2ms(kmh_speed: float) -> float:
2020
SPEED_LIMIT_112 = kph2ms(111.9996)
2121
SAFE_SPEED_30 = kph2ms(29.9988)
2222
SHORT_SLIP_SPEED_10 = kph2ms(10.0008)
23+
RELEASE_SPEED_40 = kph2ms(40)
2324

2425

2526
def _update_simulation_with_mareco_allowances(editoast_url, train_Schedule_id):
@@ -393,10 +394,10 @@ def test_etcs_schedule_result_stop_with_eoa_and_svl_at_same_location(etcs_scenar
393394
# Check that the release part (where the speed stays at 40km/h) starts and ends at the expected offsets.
394395
offset_start_release_speed = 40_827_882
395396
speed_at_start_release_speed = _get_current_or_next_speed_at(simulation_final_output, offset_start_release_speed)
396-
_assert_equal_speeds(speed_at_start_release_speed, kph2ms(40))
397+
_assert_equal_speeds(speed_at_start_release_speed, RELEASE_SPEED_40)
397398
offset_end_release_speed = 40_892_792
398399
speed_at_end_release_speed = _get_current_or_next_speed_at(simulation_final_output, offset_end_release_speed)
399-
_assert_equal_speeds(speed_at_end_release_speed, kph2ms(40))
400+
_assert_equal_speeds(speed_at_end_release_speed, RELEASE_SPEED_40)
400401

401402

402403
def test_etcs_schedule_result_stop_with_eoa_and_svl_at_different_locations(

0 commit comments

Comments
 (0)