@@ -47,57 +47,61 @@ fun addBrakingCurvesAtEOAs(
47
47
): Envelope {
48
48
val sortedEndsOfAuthority = endsOfAuthority.sortedBy { it.offsetEOA }
49
49
var beginPos = envelope.beginPos
50
- val builder = OverlayEnvelopeBuilder .forward(envelope)
50
+ val maxSpeedEnvelope = envelope.maxSpeed
51
+ var envelopeWithEoaBrakingCurves = envelope
52
+ var builder = OverlayEnvelopeBuilder .forward(envelopeWithEoaBrakingCurves)
51
53
for (endOfAuthority in sortedEndsOfAuthority) {
52
54
val targetPosition = endOfAuthority.offsetEOA.distance.meters
53
55
assert (targetPosition > 0.0 )
54
56
val targetSpeed = 0.0
55
- val maxSpeedEnvelope = envelope.maxSpeed
56
- val overhead =
57
- Envelope .make(
58
- EnvelopePart .generateTimes(
59
- listOf (EnvelopeProfile .CONSTANT_SPEED ),
60
- doubleArrayOf(0.0 , targetPosition),
61
- doubleArrayOf(maxSpeedEnvelope, maxSpeedEnvelope)
57
+ val fullIndicationCurveEoa =
58
+ computeSbdFullIndicationCurve(context, targetPosition, maxSpeedEnvelope)
59
+
60
+ if (endOfAuthority.offsetSVL != null ) {
61
+ val targetPositionSvl = endOfAuthority.offsetSVL.distance.meters
62
+ val fullIndicationCurveSvl =
63
+ computeEbdFullIndicationCurve(
64
+ context,
65
+ targetPositionSvl,
66
+ targetSpeed,
67
+ maxSpeedEnvelope
62
68
)
63
- )
64
- val sbdCurve =
65
- computeBrakingCurve(
66
- context,
67
- overhead,
68
- targetPosition,
69
- targetSpeed,
70
- BrakingType .ETCS_SBD
71
- )
72
- assert (sbdCurve.beginPos >= 0 && sbdCurve.endPos == targetPosition)
73
- assert (sbdCurve.endSpeed == targetSpeed)
74
- val guiCurve =
75
- computeBrakingCurve(
76
- context,
77
- overhead,
78
- targetPosition,
79
- targetSpeed,
80
- BrakingType .ETCS_GUI
81
- )
82
- assert (guiCurve.beginPos >= 0.0 && guiCurve.endPos == targetPosition)
83
- assert ((guiCurve.beginSpeed == maxSpeedEnvelope || guiCurve.beginPos == 0.0 ))
84
- assert (guiCurve.endSpeed == targetSpeed)
85
-
86
- val fullIndicationCurve =
87
- computeIndicationBrakingCurveFromRef(context, sbdCurve, BrakingCurveType .SBD , guiCurve)
88
- assert (fullIndicationCurve.endPos == targetPosition)
89
- assert (fullIndicationCurve.endSpeed == targetSpeed)
69
+ val indicationCurveSvl =
70
+ computeSvlIndicationCurve(
71
+ fullIndicationCurveSvl,
72
+ targetPosition,
73
+ envelope,
74
+ beginPos
75
+ )
76
+ if (indicationCurveSvl != null ) {
77
+ assert (
78
+ indicationCurveSvl.beginPos >= beginPos &&
79
+ indicationCurveSvl.endPos == targetPosition
80
+ )
81
+ assert (
82
+ indicationCurveSvl.beginSpeed <= maxSpeedEnvelope &&
83
+ indicationCurveSvl.endSpeed >= releaseSpeed
84
+ )
85
+ builder.addPart(indicationCurveSvl)
86
+ envelopeWithEoaBrakingCurves = builder.build()
87
+ builder = OverlayEnvelopeBuilder .forward(envelopeWithEoaBrakingCurves)
88
+ }
89
+ }
90
90
91
91
val indicationCurve =
92
- keepBrakingCurveUnderOverlay(Envelope .make(fullIndicationCurve), envelope, beginPos)
93
- ? : continue
92
+ keepBrakingCurveUnderOverlay(
93
+ Envelope .make(fullIndicationCurveEoa),
94
+ envelopeWithEoaBrakingCurves,
95
+ beginPos
96
+ ) ? : continue
94
97
assert (indicationCurve.beginPos >= beginPos && indicationCurve.endPos == targetPosition)
95
98
assert (
96
99
indicationCurve.beginSpeed <= maxSpeedEnvelope &&
97
100
indicationCurve.endSpeed == targetSpeed
98
101
)
99
-
100
102
builder.addPart(indicationCurve)
103
+ envelopeWithEoaBrakingCurves = builder.build()
104
+ builder = OverlayEnvelopeBuilder .forward(envelopeWithEoaBrakingCurves)
101
105
102
106
// We build EOAs along the path. We need to handle overlaps with the next EOA. To do so, we
103
107
// shift the left position constraint, beginPos, to this EOA's target position.
@@ -114,58 +118,19 @@ fun addBrakingCurvesAtLOAs(
114
118
): Envelope {
115
119
val sortedLimitsOfAuthority = limitsOfAuthority.sortedBy { it.offset }
116
120
val beginPos = envelope.beginPos
121
+ val maxSpeedEnvelope = envelope.maxSpeed
117
122
var envelopeWithLoaBrakingCurves = envelope
118
123
var builder = OverlayEnvelopeBuilder .forward(envelopeWithLoaBrakingCurves)
119
124
120
- val maxSpeedEnvelope = envelopeWithLoaBrakingCurves.maxSpeed
121
- // Add maxBecDeltaSpeed to EBD curve overhead so it reaches a sufficiently high speed to
122
- // guarantee that, after the speed translation, the corresponding EBI curve does intersect
123
- // with envelope max speed.
124
- val maxBecDeltaSpeed = maxBecDeltaSpeed()
125
- val maxSpeedEbd = maxSpeedEnvelope + maxBecDeltaSpeed
126
- val overhead =
127
- Envelope .make(
128
- EnvelopePart .generateTimes(
129
- listOf (EnvelopeProfile .CONSTANT_SPEED ),
130
- doubleArrayOf(0.0 , context.path.length),
131
- doubleArrayOf(maxSpeedEbd, maxSpeedEbd)
132
- )
133
- )
134
-
135
125
for (limitOfAuthority in sortedLimitsOfAuthority) {
136
126
val targetPosition = limitOfAuthority.offset.distance.meters
137
127
assert (targetPosition > 0.0 )
138
128
val targetSpeed = limitOfAuthority.speed
139
129
assert (targetSpeed > 0.0 )
140
-
141
- val ebdCurve =
142
- computeBrakingCurve(
143
- context,
144
- overhead,
145
- targetPosition,
146
- targetSpeed,
147
- BrakingType .ETCS_EBD
148
- )
149
- assert (ebdCurve.beginPos >= 0.0 && ebdCurve.endPos >= targetPosition)
150
- val guiCurve =
151
- computeBrakingCurve(
152
- context,
153
- overhead,
154
- targetPosition,
155
- targetSpeed,
156
- BrakingType .ETCS_GUI
157
- )
158
- assert (guiCurve.beginPos >= 0.0 && guiCurve.endPos == targetPosition)
159
- assert ((guiCurve.beginSpeed == maxSpeedEbd || guiCurve.beginPos == 0.0 ))
160
-
161
- val ebiCurve = computeEbiBrakingCurveFromEbd(context, ebdCurve, targetSpeed)
162
- assert (ebiCurve.endSpeed == targetSpeed)
163
-
164
130
val fullIndicationCurve =
165
- computeIndicationBrakingCurveFromRef (context, ebiCurve, BrakingCurveType . EBI , guiCurve )
131
+ computeEbdFullIndicationCurve (context, targetPosition, targetSpeed, maxSpeedEnvelope )
166
132
val endOfIndicationCurve = fullIndicationCurve.endPos
167
133
assert (endOfIndicationCurve <= targetPosition)
168
- assert (fullIndicationCurve.endSpeed == targetSpeed)
169
134
170
135
val fullIndCurveWithMaintain: Envelope
171
136
if (endOfIndicationCurve < targetPosition) {
@@ -203,6 +168,123 @@ fun addBrakingCurvesAtLOAs(
203
168
return envelopeWithLoaBrakingCurves
204
169
}
205
170
171
+ /* * Compute SBD-based full indication curve. */
172
+ private fun computeSbdFullIndicationCurve (
173
+ context : EnvelopeSimContext ,
174
+ targetPosition : Double ,
175
+ maxSpeedEnvelope : Double
176
+ ): EnvelopePart {
177
+ val targetSpeed = 0.0
178
+ val overhead =
179
+ Envelope .make(
180
+ EnvelopePart .generateTimes(
181
+ listOf (EnvelopeProfile .CONSTANT_SPEED ),
182
+ doubleArrayOf(0.0 , targetPosition),
183
+ doubleArrayOf(maxSpeedEnvelope, maxSpeedEnvelope)
184
+ )
185
+ )
186
+ val sbdCurve =
187
+ computeBrakingCurve(context, overhead, targetPosition, targetSpeed, BrakingType .ETCS_SBD )
188
+ assert (sbdCurve.beginPos >= 0 && sbdCurve.endPos == targetPosition)
189
+ assert (sbdCurve.endSpeed == targetSpeed)
190
+
191
+ val guiCurve =
192
+ computeBrakingCurve(context, overhead, targetPosition, targetSpeed, BrakingType .ETCS_GUI )
193
+ assert (guiCurve.beginPos >= 0.0 && guiCurve.endPos == targetPosition)
194
+ assert ((guiCurve.beginSpeed == maxSpeedEnvelope || guiCurve.beginPos == 0.0 ))
195
+ assert (guiCurve.endSpeed == targetSpeed)
196
+
197
+ val fullIndicationCurve =
198
+ computeIndicationBrakingCurveFromRef(context, sbdCurve, BrakingCurveType .SBD , guiCurve)
199
+ assert (fullIndicationCurve.endPos == targetPosition)
200
+ assert (fullIndicationCurve.endSpeed == targetSpeed)
201
+ return fullIndicationCurve
202
+ }
203
+
204
+ /* * Compute EBD-based full indication curve. */
205
+ private fun computeEbdFullIndicationCurve (
206
+ context : EnvelopeSimContext ,
207
+ targetPosition : Double ,
208
+ targetSpeed : Double ,
209
+ maxSpeedEnvelope : Double
210
+ ): EnvelopePart {
211
+ // Add maxBecDeltaSpeed to EBD curve overhead so it reaches a sufficiently high speed to
212
+ // guarantee that, after the speed translation, the corresponding EBI curve does intersect
213
+ // with envelope max speed.
214
+ val maxBecDeltaSpeed = maxBecDeltaSpeed()
215
+ val maxSpeedEbd = maxSpeedEnvelope + maxBecDeltaSpeed
216
+ val overhead =
217
+ Envelope .make(
218
+ EnvelopePart .generateTimes(
219
+ listOf (EnvelopeProfile .CONSTANT_SPEED ),
220
+ doubleArrayOf(0.0 , max(context.path.length, targetPosition)),
221
+ doubleArrayOf(maxSpeedEbd, maxSpeedEbd)
222
+ )
223
+ )
224
+
225
+ val ebdCurve =
226
+ computeBrakingCurve(context, overhead, targetPosition, targetSpeed, BrakingType .ETCS_EBD )
227
+ assert (ebdCurve.beginPos >= 0.0 && ebdCurve.endPos >= targetPosition)
228
+ // TODO: uncomment once ebd is not shifted anymore for an LoA
229
+ // assert((ebdCurve.beginSpeed == maxSpeedEbd || ebdCurve.beginPos == 0.0))
230
+
231
+ val guiCurve =
232
+ computeBrakingCurve(context, overhead, targetPosition, targetSpeed, BrakingType .ETCS_GUI )
233
+ assert (guiCurve.beginPos >= 0.0 && guiCurve.endPos == targetPosition)
234
+ assert ((guiCurve.beginSpeed == maxSpeedEbd || guiCurve.beginPos == 0.0 ))
235
+
236
+ val ebiCurve = computeEbiBrakingCurveFromEbd(context, ebdCurve, targetSpeed)
237
+ assert (ebiCurve.endSpeed == targetSpeed)
238
+
239
+ val fullIndicationCurve =
240
+ computeIndicationBrakingCurveFromRef(context, ebiCurve, BrakingCurveType .EBI , guiCurve)
241
+ assert (fullIndicationCurve.endSpeed == targetSpeed)
242
+ return fullIndicationCurve
243
+ }
244
+
245
+ private fun computeSvlIndicationCurve (
246
+ fullIndicationCurveSvl : EnvelopePart ,
247
+ targetPosition : Double ,
248
+ overlay : Envelope ,
249
+ beginPos : Double
250
+ ): EnvelopePart ? {
251
+ // If no part of the indication curve is before the target position, ignore it.
252
+ if (fullIndicationCurveSvl.beginPos >= targetPosition) return null
253
+ val fullIndCurveSvlWithMaintain: Envelope
254
+ val releaseSpeedPosition =
255
+ if (fullIndicationCurveSvl.maxSpeed >= releaseSpeed)
256
+ fullIndicationCurveSvl.interpolatePosition(releaseSpeed)
257
+ else fullIndicationCurveSvl.beginPos
258
+ if (releaseSpeedPosition >= targetPosition) {
259
+ fullIndCurveSvlWithMaintain =
260
+ Envelope .make(
261
+ fullIndicationCurveSvl.slice(fullIndicationCurveSvl.beginPos, targetPosition)
262
+ )
263
+ } else {
264
+ val maintainReleaseSpeedCurve =
265
+ EnvelopePart .generateTimes(
266
+ listOf (EnvelopeProfile .CONSTANT_SPEED ),
267
+ doubleArrayOf(releaseSpeedPosition, targetPosition),
268
+ doubleArrayOf(releaseSpeed, releaseSpeed)
269
+ )
270
+ fullIndCurveSvlWithMaintain =
271
+ if (releaseSpeedPosition == fullIndicationCurveSvl.beginPos) {
272
+ Envelope .make(maintainReleaseSpeedCurve)
273
+ } else {
274
+ Envelope .make(
275
+ fullIndicationCurveSvl.sliceWithSpeeds(
276
+ fullIndicationCurveSvl.beginPos,
277
+ fullIndicationCurveSvl.beginSpeed,
278
+ releaseSpeedPosition,
279
+ releaseSpeed
280
+ ),
281
+ maintainReleaseSpeedCurve
282
+ )
283
+ }
284
+ }
285
+ return keepBrakingCurveUnderOverlay(fullIndCurveSvlWithMaintain, overlay, beginPos)
286
+ }
287
+
206
288
/* * Compute braking curve: used to compute EBD, SBD or GUI. */
207
289
private fun computeBrakingCurve (
208
290
context : EnvelopeSimContext ,
@@ -217,10 +299,10 @@ private fun computeBrakingCurve(
217
299
brakingType == BrakingType .ETCS_GUI
218
300
)
219
301
// If the stopPosition is after the end of the path, the input is invalid except if it is an
220
- // SVL, i.e. the target speed is 0 and the curve to compute is an EBD .
302
+ // SVL, i.e. the target speed is 0 and the curve to compute is not an SBD .
221
303
if (
222
304
(targetPosition > context.path.length &&
223
- ! (targetSpeed == 0.0 && brakingType == BrakingType .ETCS_EBD ))
305
+ (targetSpeed != 0.0 || brakingType == BrakingType .ETCS_SBD ))
224
306
)
225
307
throw RuntimeException (
226
308
String .format(
@@ -282,15 +364,21 @@ private fun computeEbiBrakingCurveFromEbd(
282
364
targetSpeed : Double
283
365
): EnvelopePart {
284
366
val pointCount = ebdCurve.pointCount()
285
- val newPositions = DoubleArray (pointCount)
286
- val newSpeeds = DoubleArray (pointCount)
287
- for (i in 0 until ebdCurve. pointCount() ) {
367
+ var newPositions = DoubleArray (pointCount)
368
+ var newSpeeds = DoubleArray (pointCount)
369
+ for (i in 0 until pointCount) {
288
370
val speed = ebdCurve.getPointSpeed(i)
289
371
val becParams = computeBecParams(context, ebdCurve, speed, targetSpeed)
290
372
val newPos = ebdCurve.getPointPos(i) - becParams.dBec
291
373
val newSpeed = speed - becParams.deltaBecSpeed
292
374
newPositions[i] = newPos
293
- newSpeeds[i] = newSpeed
375
+ // TODO: unneeded for now: interpolate to not approximate position at 0 m/s.
376
+ newSpeeds[i] = max(newSpeed, 0.0 )
377
+ if (newSpeed <= 0.0 && i < pointCount - 1 ) {
378
+ newPositions = newPositions.dropLast(pointCount - 1 - i).toDoubleArray()
379
+ newSpeeds = newSpeeds.dropLast(pointCount - 1 - i).toDoubleArray()
380
+ break
381
+ }
294
382
}
295
383
296
384
val fullBrakingCurve =
@@ -353,12 +441,25 @@ private fun keepBrakingCurveUnderOverlay(
353
441
overlay : Envelope ,
354
442
beginPos : Double
355
443
): EnvelopePart ? {
356
- if (fullBrakingCurve.endPos <= beginPos) {
444
+ if (fullBrakingCurve.beginPos >= overlay.endPos || fullBrakingCurve. endPos <= beginPos) {
357
445
etcsBrakingCurvesLogger.warn(
358
- " The position-range of the ETCS braking curve ending at ($beginPos , ${fullBrakingCurve.endSpeed} ) does not intersect with the overlay envelope's position-range."
446
+ " The position-range of the ETCS braking curve starting at (${fullBrakingCurve. beginPos} , ${fullBrakingCurve.beginSpeed} ) and ending at ( ${fullBrakingCurve.endPos} , ${fullBrakingCurve.endSpeed} ) does not intersect with the overlay envelope's position-range."
359
447
)
360
448
return null
361
449
}
450
+ if (
451
+ fullBrakingCurve.minSpeed >
452
+ Envelope .make(
453
+ * overlay.slice(
454
+ max(fullBrakingCurve.beginPos, beginPos),
455
+ min(fullBrakingCurve.endPos, overlay.endPos)
456
+ )
457
+ )
458
+ .maxSpeed
459
+ ) {
460
+ // The full braking curve is above the overlay envelope: nothing to do here.
461
+ return null
462
+ }
362
463
363
464
// Remove duplicate point part transitions: the last point of the previous array is the first
364
465
// point of the next array. Otherwise, we would be adding two following identical points with
@@ -383,11 +484,23 @@ private fun keepBrakingCurveUnderOverlay(
383
484
val overlayBuilder =
384
485
ConstrainedEnvelopePartBuilder (
385
486
partBuilder,
386
- PositionConstraint (beginPos, overlay.endPos),
487
+ PositionConstraint (max( beginPos, fullBrakingCurve.beginPos) , overlay.endPos),
387
488
EnvelopeConstraint (overlay, EnvelopePartConstraintType .CEILING )
388
489
)
389
- overlayBuilder.initEnvelopePart(positions[nbPoints - 1 ], speeds[nbPoints - 1 ], - 1.0 )
390
- for (i in nbPoints - 2 downTo 0 ) {
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
493
+ while (
494
+ speeds[lastIndex] >
495
+ overlay
496
+ .get(overlay.findRightDir(positions[lastIndex], - 1.0 ))
497
+ .interpolateSpeed(positions[lastIndex])
498
+ ) {
499
+ 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 ) {
391
504
if (! overlayBuilder.addStep(positions[i], speeds[i], timeDeltas[i])) break
392
505
}
393
506
return partBuilder.build()
0 commit comments