From 9d0af55b51edabd3605242f844a2d3e3c989abf5 Mon Sep 17 00:00:00 2001 From: Erashin Date: Tue, 14 Jan 2025 10:58:13 +0100 Subject: [PATCH] fixup! core: implement etcs braking simulator Signed-off-by: Erashin --- core/envelope-sim/build.gradle | 1 + .../sncf/osrd/envelope_sim/PhysicsPath.java | 2 +- .../envelope_sim/PhysicsRollingStock.java | 3 +- .../envelope_sim/TrainPhysicsIntegrator.java | 3 + .../sncf/osrd/envelope_sim/etcs/Constants.kt | 1 + .../envelope_sim/etcs/ETCSBrakingCurves.kt | 62 ++++++++++++------- .../envelope_sim/etcs/ETCSBrakingSimulator.kt | 6 ++ .../osrd/envelope_sim/SimpleRollingStock.java | 1 - core/osrd-railjson/build.gradle | 5 +- .../rollingstock/RJSEtcsBrakeParams.java | 20 ++++-- .../java/fr/sncf/osrd/train/RollingStock.java | 1 + 11 files changed, 71 insertions(+), 34 deletions(-) diff --git a/core/envelope-sim/build.gradle b/core/envelope-sim/build.gradle index 4aafebb47cf..5a7422c00d4 100644 --- a/core/envelope-sim/build.gradle +++ b/core/envelope-sim/build.gradle @@ -20,6 +20,7 @@ dependencies { implementation project(':osrd-reporting') implementation project(":kt-osrd-sim-infra") + api project(":osrd-railjson") api project(":kt-osrd-utils") implementation libs.guava implementation libs.slf4j diff --git a/core/envelope-sim/src/main/java/fr/sncf/osrd/envelope_sim/PhysicsPath.java b/core/envelope-sim/src/main/java/fr/sncf/osrd/envelope_sim/PhysicsPath.java index cf354d1fa08..8acd441e78c 100644 --- a/core/envelope-sim/src/main/java/fr/sncf/osrd/envelope_sim/PhysicsPath.java +++ b/core/envelope-sim/src/main/java/fr/sncf/osrd/envelope_sim/PhysicsPath.java @@ -7,6 +7,6 @@ public interface PhysicsPath { /** The average slope on a given range, in m/km */ double getAverageGrade(double begin, double end); - /** The lowest slope on a given range, in m/km*/ + /** The lowest slope on a given range, in m/km */ double getMinGrade(double begin, double end); } diff --git a/core/envelope-sim/src/main/java/fr/sncf/osrd/envelope_sim/PhysicsRollingStock.java b/core/envelope-sim/src/main/java/fr/sncf/osrd/envelope_sim/PhysicsRollingStock.java index 27a377f30f8..401b6d93f70 100644 --- a/core/envelope-sim/src/main/java/fr/sncf/osrd/envelope_sim/PhysicsRollingStock.java +++ b/core/envelope-sim/src/main/java/fr/sncf/osrd/envelope_sim/PhysicsRollingStock.java @@ -61,7 +61,8 @@ static double getMaxEffort(double speed, TractiveEffortPoint[] tractiveEffortCur /** * The gradient acceleration of the rolling stock taking its rotating mass into account, in m/s². * Grade is in m/km. - * */ + * mRotating (Max or Min) is in %, as seen in ERA braking curves simulation tool v5.1. + */ static double getGradientAcceleration(double grade) { var mRotating = grade >= 0 ? mRotatingMax : mRotatingMin; return -GRAVITY_ACCELERATION * grade / (1000.0 + 10.0 * mRotating); diff --git a/core/envelope-sim/src/main/java/fr/sncf/osrd/envelope_sim/TrainPhysicsIntegrator.java b/core/envelope-sim/src/main/java/fr/sncf/osrd/envelope_sim/TrainPhysicsIntegrator.java index accd9753ef6..8f0544cea2c 100644 --- a/core/envelope-sim/src/main/java/fr/sncf/osrd/envelope_sim/TrainPhysicsIntegrator.java +++ b/core/envelope-sim/src/main/java/fr/sncf/osrd/envelope_sim/TrainPhysicsIntegrator.java @@ -115,10 +115,13 @@ private double getDeceleration(double speed, double position) { var grade = getMinGrade(rollingStock, path, position); var gradientAcceleration = getGradientAcceleration(grade); return switch (brakingType) { + // See Subset referenced in RJSEtcsBrakeParams: §3.13.6.2.1.3. case ETCS_EBD -> -rollingStock.getRJSEtcsBrakeParams().getSafeBrakingAcceleration(speed) + gradientAcceleration; + // See Subset referenced in RJSEtcsBrakeParams: §3.13.6.3.1.3. case ETCS_SBD -> -rollingStock.getRJSEtcsBrakeParams().getServiceBrakingAcceleration(speed) + gradientAcceleration; + // See Subset referenced in RJSEtcsBrakeParams: §3.13.6.4.3. case ETCS_GUI -> -rollingStock.getRJSEtcsBrakeParams().getNormalServiceBrakingAcceleration(speed) + gradientAcceleration + rollingStock.getRJSEtcsBrakeParams().getGradientAccelerationCorrection(grade, speed); 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 d5e1c1f8969..c6e4fbfc97b 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,5 +1,6 @@ package fr.sncf.osrd.envelope_sim.etcs +/** See Subset referenced in ETCSBrakingSimulator: table in Appendix A.3.1. */ const val tDriver = 4.0 // s const val mRotatingMax = 15.0 // % const val mRotatingMin = 2.0 // % 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 511b6888be6..579ae265cf7 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 @@ -129,37 +129,46 @@ private fun computeBrakingCurve( return brakingCurve } -/** Compute Indication curve: EBI/SBD -> SBI -> PS -> IND */ +/** + * Compute Indication curve: EBI/SBD -> SBI -> PS -> IND. See Subset referenced in + * ETCSBrakingSimulator: figures 45 and 46. + */ private fun computeIndicationBrakingCurveFromRef( context: EnvelopeSimContext, - refCurve: EnvelopePart, + refBrakingCurve: EnvelopePart, refBrakingCurveType: BrakingCurveType, guiCurve: EnvelopePart, beginPos: Double ): EnvelopePart { - assert( - refBrakingCurveType == BrakingCurveType.EBI || refBrakingCurveType == BrakingCurveType.SBD - ) - assert(refCurve.endPos > beginPos) + assert(refBrakingCurve.endPos > beginPos) val rollingStock = context.rollingStock val tBs = - if (refBrakingCurveType == BrakingCurveType.EBI) rollingStock.rjsEtcsBrakeParams.tBs2 - else rollingStock.rjsEtcsBrakeParams.tBs1 + when (refBrakingCurveType) { + BrakingCurveType.EBI -> rollingStock.rjsEtcsBrakeParams.tBs2 + BrakingCurveType.SBD -> rollingStock.rjsEtcsBrakeParams.tBs1 + else -> + throw IllegalArgumentException( + "Expected EBI or SBD reference braking curve type, found: $refBrakingCurveType" + ) + } - val pos = refCurve.clonePositions() - val speeds = refCurve.cloneSpeeds() val reversedNewPos = ArrayList() val reversedNewSpeeds = ArrayList() - for (i in refCurve.pointCount() - 1 downTo 0) { - val speed = speeds[i] - val sbiPosition = getSbiPosition(pos[i], speed, tBs) - val permittedSpeedPosition = - getPermittedSpeedPosition(sbiPosition, speed, guiCurve.interpolatePosition(speed)) - val indicationPosition = getIndicationPosition(permittedSpeedPosition, speed, tBs) + for (i in refBrakingCurve.pointCount() - 1 downTo 0) { + 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) + ) + val indicationPosition = getIndicationPosition(adjustedPermittedSpeedPosition, speed, tBs) if (indicationPosition >= beginPos) { reversedNewPos.add(indicationPosition) reversedNewSpeeds.add(speed) - } else if (i != refCurve.pointCount() - 1 && reversedNewPos[i + 1] > beginPos) { + } else if (i != refBrakingCurve.pointCount() - 1 && reversedNewPos[i + 1] > beginPos) { + // Interpolate to begin position if reaching a position before it val prevPos = reversedNewPos[i + 1] val prevSpeed = reversedNewSpeeds[i + 1] val speedAtBeginPos = @@ -215,21 +224,28 @@ private fun keepBrakingCurveUnderOverlay( return partBuilder.build() } +/** See Subset referenced in ETCSBrakingSimulator: §3.13.9.3.3.1 and §3.13.9.3.3.2. */ fun getSbiPosition(ebiOrSbdPosition: Double, speed: Double, tbs: Double): Double { return getPreviousPosition(ebiOrSbdPosition, speed, tbs) } -fun getPermittedSpeedPosition( - sbiPosition: Double, - speed: Double, +/** See Subset referenced in ETCSBrakingSimulator: §3.13.9.3.5.1. */ +fun getPermittedSpeedPosition(sbiPosition: Double, speed: Double): Double { + return getPreviousPosition(sbiPosition, speed, tDriver) +} + +/** See Subset referenced in ETCSBrakingSimulator: §3.13.9.3.5.4. */ +fun getAdjustedPermittedSpeedPosition( + permittedSpeedPosition: Double, guiPosition: Double = Double.POSITIVE_INFINITY ): Double { - return min(getPreviousPosition(sbiPosition, speed, tDriver), guiPosition) + return min(permittedSpeedPosition, guiPosition) } -fun getIndicationPosition(psPosition: Double, speed: Double, tBs: Double): Double { +/** See Subset referenced in ETCSBrakingSimulator: §3.13.9.3.6.1 and §3.13.9.3.6.2. */ +fun getIndicationPosition(permittedSpeedPosition: Double, speed: Double, tBs: Double): Double { val tIndication = max((0.8 * tBs), 5.0) + tDriver - return getPreviousPosition(psPosition, speed, tIndication) + return getPreviousPosition(permittedSpeedPosition, speed, tIndication) } private fun getPreviousPosition(position: Double, speed: Double, elapsedTime: Double): 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 bf623d1ce4d..e73ed9d69a7 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 @@ -6,6 +6,11 @@ import fr.sncf.osrd.sim_infra.api.Path import fr.sncf.osrd.sim_infra.api.TravelledPath import fr.sncf.osrd.utils.units.Offset +/** + * In charge of computing and adding the ETCS braking curves. Formulas are found in `SUBSET-026-3 + * v400.pdf` from the file at + * https://www.era.europa.eu/system/files/2023-09/index004_-_SUBSET-026_v400.zip + */ interface ETCSBrakingSimulator { val context: EnvelopeSimContext @@ -55,6 +60,7 @@ class ETCSBrakingSimulatorImpl(override val context: EnvelopeSimContext) : ETCSB endsOfAuthority: Collection ): Envelope { if (endsOfAuthority.isEmpty()) return envelope + // TODO: compute curve at SvL and intersect return addBrakingCurvesAtEOAs(envelope, context, endsOfAuthority) } } diff --git a/core/envelope-sim/src/testFixtures/java/fr/sncf/osrd/envelope_sim/SimpleRollingStock.java b/core/envelope-sim/src/testFixtures/java/fr/sncf/osrd/envelope_sim/SimpleRollingStock.java index 165c3f06521..ac546adfce7 100644 --- a/core/envelope-sim/src/testFixtures/java/fr/sncf/osrd/envelope_sim/SimpleRollingStock.java +++ b/core/envelope-sim/src/testFixtures/java/fr/sncf/osrd/envelope_sim/SimpleRollingStock.java @@ -4,7 +4,6 @@ import com.google.common.collect.Range; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import fr.sncf.osrd.railjson.schema.rollingstock.RJSEtcsBrakeParams; - import java.util.ArrayList; @SuppressFBWarnings({"URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD"}) diff --git a/core/osrd-railjson/build.gradle b/core/osrd-railjson/build.gradle index 50fcea14c79..819c03fc00b 100644 --- a/core/osrd-railjson/build.gradle +++ b/core/osrd-railjson/build.gradle @@ -1,4 +1,5 @@ plugins { + alias(libs.plugins.kotlin.jvm) id 'java' id 'jacoco' } @@ -20,8 +21,8 @@ dependencies { implementation libs.hppc // JSON parsing - implementation libs.moshi - implementation libs.moshi.adapters + api libs.moshi + api libs.moshi.adapters implementation libs.guava diff --git a/core/osrd-railjson/src/main/java/fr/sncf/osrd/railjson/schema/rollingstock/RJSEtcsBrakeParams.java b/core/osrd-railjson/src/main/java/fr/sncf/osrd/railjson/schema/rollingstock/RJSEtcsBrakeParams.java index 44ced9e2035..872e05dbb4a 100644 --- a/core/osrd-railjson/src/main/java/fr/sncf/osrd/railjson/schema/rollingstock/RJSEtcsBrakeParams.java +++ b/core/osrd-railjson/src/main/java/fr/sncf/osrd/railjson/schema/rollingstock/RJSEtcsBrakeParams.java @@ -9,11 +9,8 @@ */ public class RJSEtcsBrakeParams { - /** National Default Value: Available Adhesion */ - private final double mNvavadh = 0.0; - - /** National Default Value: Emergency Brake Confidence Level in % */ - private final double mNvebcl = 1 - 1E-9; + /** National Default Value: Available Adhesion. Found in Subset Appendix A.3.2 table. */ + private static final double mNvavadh = 0.0; // A_brake_emergency: the emergency deceleration curve (values > 0 m/s²) @Json(name = "gamma_emergency") @@ -66,6 +63,7 @@ public class RJSEtcsBrakeParams { @Json(name = "t_be") public double tBe; + /** See Subset §3.13.6.2.1.4. */ public double getSafeBrakingAcceleration(double speed) { var aBrakeEmergency = getEmergencyBrakingDeceleration(speed); var kDry = getRollingStockCorrectionFactorDry(speed); @@ -77,10 +75,16 @@ private double getEmergencyBrakingDeceleration(double speed) { return gammaEmergency.getValue(speed); } + /** + * Corresponds to the correction factor of the emergency brake deceleration on dry tracks. + * The confidence level mNvebcl is the confidence level that the corresponding deceleration can be reached, + * but does not impact the calculation of kDry. See Subset §3.13.6.2.1.7. + */ private double getRollingStockCorrectionFactorDry(double speed) { - return kDry.getValue(speed) * mNvebcl; + return kDry.getValue(speed); } + /** Corresponds to the correction factor of the emergency brake deceleration on wet tracks. */ private double getRollingStockCorrectionFactorWet(double speed) { return kWet.getValue(speed); } @@ -93,6 +97,10 @@ public double getNormalServiceBrakingAcceleration(double speed) { return gammaNormalService.getValue(speed); } + /** + * Gradient acceleration correction using on-board correction factors kN+ and kN-. + * See Subset, §3.13.6.4.2 and §3.13.6.4.3. + */ public double getGradientAccelerationCorrection(double grade, double speed) { var k = grade >= 0 ? kNPos.getValue(speed) : kNNeg.getValue(speed); return -k * grade / 1000; diff --git a/core/src/main/java/fr/sncf/osrd/train/RollingStock.java b/core/src/main/java/fr/sncf/osrd/train/RollingStock.java index 94523fd8806..b40c4352d95 100644 --- a/core/src/main/java/fr/sncf/osrd/train/RollingStock.java +++ b/core/src/main/java/fr/sncf/osrd/train/RollingStock.java @@ -124,6 +124,7 @@ public double getRollingResistanceDeriv(double speed) { @Override public RJSEtcsBrakeParams getRJSEtcsBrakeParams() { + assert etcsBrakeParams != null; return etcsBrakeParams; }