@@ -12,14 +12,32 @@ import fr.sncf.osrd.envelope_sim.EnvelopeProfile
12
12
import fr.sncf.osrd.envelope_sim.EnvelopeSimContext
13
13
import fr.sncf.osrd.envelope_sim.StopMeta
14
14
import fr.sncf.osrd.envelope_sim.TrainPhysicsIntegrator
15
+ import fr.sncf.osrd.envelope_sim.etcs.ETCSBrakingSimulator
16
+ import fr.sncf.osrd.envelope_sim.etcs.ETCSBrakingSimulatorImpl
17
+ import fr.sncf.osrd.envelope_sim.etcs.EndOfAuthority
18
+ import fr.sncf.osrd.envelope_sim.etcs.LimitOfAuthority
15
19
import fr.sncf.osrd.envelope_sim.overlays.EnvelopeDeceleration
16
20
import fr.sncf.osrd.reporting.exceptions.OSRDError
21
+ import fr.sncf.osrd.sim_infra.api.Path
22
+ import fr.sncf.osrd.utils.units.Offset
23
+ import fr.sncf.osrd.utils.units.meters
17
24
18
25
/* *
19
26
* Max speed envelope = MRSP + braking curves It is the max speed allowed at any given point,
20
27
* ignoring allowances
21
28
*/
22
29
object MaxSpeedEnvelope {
30
+
31
+ /* *
32
+ * Simple data class for easier processing, local to this file. Combines the stop offset with
33
+ * the "etcs" flag.
34
+ */
35
+ private data class SimStop (
36
+ val offset : Double ,
37
+ val isETCS : Boolean ,
38
+ val index : Int , // Index in the stop list
39
+ )
40
+
23
41
fun increase (prevPos : Double , prevSpeed : Double , nextPos : Double , nextSpeed : Double ): Boolean {
24
42
// Works for both accelerations (forwards) and decelerations (backwards)
25
43
return prevSpeed < nextSpeed
@@ -29,10 +47,16 @@ object MaxSpeedEnvelope {
29
47
* Generate braking curves overlay everywhere the mrsp decrease (increase backwards) with a
30
48
* discontinuity
31
49
*/
32
- private fun addBrakingCurves (context : EnvelopeSimContext , mrsp : Envelope ): Envelope {
33
- val builder = OverlayEnvelopeBuilder .backward(mrsp)
34
- val cursor = EnvelopeCursor .backward(mrsp)
35
- var lastPosition = mrsp.endPos
50
+ private fun addBrakingCurves (
51
+ etcsSimulator : ETCSBrakingSimulator ,
52
+ context : EnvelopeSimContext ,
53
+ mrsp : Envelope
54
+ ): Envelope {
55
+ var envelope = mrsp
56
+ envelope = addETCSBrakingCurves(etcsSimulator, context, envelope)
57
+ val builder = OverlayEnvelopeBuilder .backward(envelope)
58
+ val cursor = EnvelopeCursor .backward(envelope)
59
+ var lastPosition = envelope.endPos
36
60
while (cursor.findPartTransition(MaxSpeedEnvelope ::increase)) {
37
61
if (cursor.getPosition() > lastPosition) {
38
62
// The next braking curve already covers this point, this braking curve is hidden
@@ -45,7 +69,7 @@ object MaxSpeedEnvelope {
45
69
ConstrainedEnvelopePartBuilder (
46
70
partBuilder,
47
71
SpeedConstraint (0.0 , EnvelopePartConstraintType .FLOOR ),
48
- EnvelopeConstraint (mrsp , EnvelopePartConstraintType .CEILING )
72
+ EnvelopeConstraint (envelope , EnvelopePartConstraintType .CEILING )
49
73
)
50
74
val startSpeed = cursor.getSpeed()
51
75
val startPosition = cursor.getPosition()
@@ -64,51 +88,113 @@ object MaxSpeedEnvelope {
64
88
return builder.build()
65
89
}
66
90
91
+ /* * Add braking curves following ETCS rules in relevant places */
92
+ private fun addETCSBrakingCurves (
93
+ etcsSimulator : ETCSBrakingSimulator ,
94
+ context : EnvelopeSimContext ,
95
+ envelope : Envelope
96
+ ): Envelope {
97
+ val etcsRanges = context.etcsContext?.brakingRanges ? : return envelope
98
+ val cursor = EnvelopeCursor .backward(envelope)
99
+ val limitsOfAuthority = mutableListOf<LimitOfAuthority >()
100
+ while (cursor.findPartTransition(MaxSpeedEnvelope ::increase)) {
101
+ val offset = Offset <Path >(cursor.position.meters)
102
+ if (etcsRanges.contains(offset.distance)) {
103
+ limitsOfAuthority.add(
104
+ LimitOfAuthority (
105
+ offset,
106
+ cursor.speed,
107
+ )
108
+ )
109
+ }
110
+ }
111
+ return etcsSimulator.addETCSBrakingParts(envelope, limitsOfAuthority, listOf ())
112
+ }
113
+
67
114
/* * Generate braking curves overlay at every stop position */
68
115
private fun addStopBrakingCurves (
116
+ etcsSimulator : ETCSBrakingSimulator ,
69
117
context : EnvelopeSimContext ,
70
118
stopPositions : DoubleArray ,
71
119
curveWithDecelerations : Envelope
72
120
): Envelope {
73
- var curveWithDecelerations = curveWithDecelerations
74
- for (i in stopPositions.indices) {
75
- var stopPosition = stopPositions[i]
76
- // if the stopPosition is zero, no need to build a deceleration curve
77
- if (stopPosition == 0.0 ) continue
78
- if (stopPosition > curveWithDecelerations.endPos) {
79
- if (
80
- TrainPhysicsIntegrator .arePositionsEqual(
81
- stopPosition,
82
- curveWithDecelerations.endPos
83
- )
84
- )
85
- stopPosition = curveWithDecelerations.endPos
86
- else
87
- throw OSRDError .newEnvelopeError(i, stopPosition, curveWithDecelerations.endPos)
88
- }
121
+ var envelope = curveWithDecelerations
122
+ val stops = makeSimStops(context, stopPositions, envelope)
123
+ envelope = addETCSStops(etcsSimulator, context, envelope, stops)
124
+ for (stop in stops) {
125
+ if (stop.isETCS) continue // Already handled
89
126
val partBuilder = EnvelopePartBuilder ()
90
127
partBuilder.setAttr<EnvelopeProfile >(EnvelopeProfile .BRAKING )
91
- partBuilder.setAttr<StopMeta >(StopMeta (i ))
128
+ partBuilder.setAttr<StopMeta >(StopMeta (stop.index ))
92
129
val overlayBuilder =
93
130
ConstrainedEnvelopePartBuilder (
94
131
partBuilder,
95
132
SpeedConstraint (0.0 , EnvelopePartConstraintType .FLOOR ),
96
- EnvelopeConstraint (curveWithDecelerations , EnvelopePartConstraintType .CEILING )
133
+ EnvelopeConstraint (envelope , EnvelopePartConstraintType .CEILING )
97
134
)
98
- EnvelopeDeceleration .decelerate(context, stopPosition , 0.0 , overlayBuilder, - 1.0 )
135
+ EnvelopeDeceleration .decelerate(context, stop.offset , 0.0 , overlayBuilder, - 1.0 )
99
136
100
- val builder = OverlayEnvelopeBuilder .backward(curveWithDecelerations )
137
+ val builder = OverlayEnvelopeBuilder .backward(envelope )
101
138
builder.addPart(partBuilder.build())
102
- curveWithDecelerations = builder.build()
139
+ envelope = builder.build()
140
+ }
141
+ return envelope
142
+ }
143
+
144
+ /* * Add braking parts for any ETCS flagged stop. */
145
+ private fun addETCSStops (
146
+ simulator : ETCSBrakingSimulator ,
147
+ context : EnvelopeSimContext ,
148
+ envelope : Envelope ,
149
+ stops : List <SimStop >
150
+ ): Envelope {
151
+ val endsOfAuthority =
152
+ stops
153
+ .filter { it.isETCS }
154
+ .map { EndOfAuthority (Offset (it.offset.meters), getDangerPoint(context, it)) }
155
+ return simulator.addETCSBrakingParts(envelope, listOf (), endsOfAuthority)
156
+ }
157
+
158
+ /* *
159
+ * Returns the SVL location: next buffer stop or switch, whichever is closest. If there is any.
160
+ */
161
+ private fun getDangerPoint (context : EnvelopeSimContext , stop : SimStop ): Offset <Path >? {
162
+ val etcsContext = context.etcsContext!!
163
+ return etcsContext.dangerPointOffsets.firstOrNull { it.distance.meters >= stop.offset }
164
+ }
165
+
166
+ /* *
167
+ * Converts the raw double offsets into a data class with some metadata. Handles some of the
168
+ * input checking (such as invalid offsets).
169
+ */
170
+ private fun makeSimStops (
171
+ context : EnvelopeSimContext ,
172
+ stopOffsets : DoubleArray ,
173
+ envelope : Envelope
174
+ ): List <SimStop > {
175
+ val res = mutableListOf<SimStop >()
176
+ for ((i, stopOffset) in stopOffsets.withIndex()) {
177
+ if (stopOffset == 0.0 ) continue
178
+ val isETCS = context.etcsContext?.brakingRanges?.contains(stopOffset.meters) ? : false
179
+ var offset = stopOffset
180
+ if (offset > envelope.endPos) {
181
+ if (TrainPhysicsIntegrator .arePositionsEqual(offset, envelope.endPos))
182
+ offset = envelope.endPos
183
+ else throw OSRDError .newEnvelopeError(i, offset, envelope.endPos)
184
+ }
185
+ res.add(SimStop (offset, isETCS, i))
103
186
}
104
- return curveWithDecelerations
187
+ return res
105
188
}
106
189
107
190
/* * Generate a max speed envelope given a mrsp */
108
191
@JvmStatic
109
192
fun from (context : EnvelopeSimContext , stopPositions : DoubleArray , mrsp : Envelope ): Envelope {
110
- var maxSpeedEnvelope = addBrakingCurves(context, mrsp)
111
- maxSpeedEnvelope = addStopBrakingCurves(context, stopPositions, maxSpeedEnvelope)
193
+ val etcsSimulator =
194
+ ETCSBrakingSimulatorImpl (context.path, context.rollingStock, context.timeStep)
195
+ var maxSpeedEnvelope = addBrakingCurves(etcsSimulator, context, mrsp)
196
+ maxSpeedEnvelope =
197
+ addStopBrakingCurves(etcsSimulator, context, stopPositions, maxSpeedEnvelope)
112
198
return maxSpeedEnvelope
113
199
}
114
200
}
0 commit comments