lsa-planer

LSA-Planer Professional – Planungssoftware für Lichtsignalanlagen nach RiLSA 2015 und § 45 StVO. EUPL-1.2.

/ tests domain bezugswerteVerfahren.test.ts

8,2 KB Rohdatei
tests/domain/bezugswerteVerfahren.test.ts — 188 Zeilen
1 import { describe, expect, it } from 'vitest';
2 import { ueberfahrzeitHerkunft } from '@/domain/rilsa/ansaetze';
3 import {
4 RILSA_DEFAULTS,
5 RILSA_ENGSTELLE,
6 UEBERFAHRZEIT_ANSATZ_LABELS,
7 } from '@/domain/rilsa/constants';
8 import { bezugswerte } from '@/domain/rilsa/intergreen';
9 import { engstellenRaeumgeschwindigkeitMs } from '@/domain/rilsa/engstellensignalisierung';
10
11 /**
12 * Die Bezugswerte richten sich nach dem Verfahren, nicht nach der Gewohnheit.
13 *
14 * ANLASS (Befund aus dem Betrieb, 11.09.2026). Das Zwischenzeitfenster einer
15 * einstreifigen Verkehrsfuehrung nannte unter dem Feld "Raeumgeschwindigkeit"
16 * den Regelwert der KNOTENPUNKTRECHNUNG - 10,0 m/s - und wies zwei Zeilen
17 * tiefer im Rechenweg desselben Fensters 11,11 m/s aus. An einer Engstelle
18 * rechnet der Fachkern nach RiLSA 2015, Abschnitt 5.2.2, also mit der
19 * Raeumgeschwindigkeitsstaffel und nicht mit dem Vorgabenwert; die
20 * Beschriftung holte ihren Wert dagegen aus `regelRaeumgeschwindigkeit`, und
21 * die Funktion kennt die Anlagenart nicht.
22 *
23 * WARUM DAS MEHR IST ALS EIN SCHOENHEITSFEHLER. Der genannte Wert ist die
24 * Groesse, gegen die der Bearbeiter seine Handeingabe abwaegt und im Zweifel
25 * begruendet. 10 m/s sieht strenger aus als die 11,11 m/s des Verfahrens - eine
26 * Handeingabe daneben erschien deshalb harmloser, als sie ist. Das ist die
27 * unsichere Richtung.
28 *
29 * ZWEI NACHBARFELDER HAENGEN MIT DRAN. Die Ueberfahrzeit steht an der Engstelle
30 * mit 4 s fest im Abschnitt und ist nicht die Gelbzeit. Und die
31 * Einfahrgeschwindigkeit geht dort in gar keine Zahl ein, weil der Abschnitt
32 * ausdruecklich ohne Einfahrzeit rechnet - ein Regelwert unter einem Feld ohne
33 * Wirkung laedt dazu ein, daran zu drehen.
34 */
35
36 /** Der gemeldete Fall: 50 km/h zulaessig, Raeumgeschwindigkeit der Staffel 40 km/h. */
37 const KFZ_GERADEAUS = {
38 mode: 'kfz',
39 movement: 'geradeaus',
40 vZul: 50,
41 } as const;
42
43 describe('Am Knotenpunkt bleibt alles beim Regelwert der Knotenpunktrechnung', () => {
44 it('nennt die Raeumgeschwindigkeit des Kennwertsatzes', () => {
45 const werte = bezugswerte('knotenpunkt', KFZ_GERADEAUS, 'kfz', RILSA_DEFAULTS);
46 expect(werte.raeumgeschwindigkeit).toBe(RILSA_DEFAULTS.clearingSpeed.kfzStraight);
47 });
48
49 it('nennt eine Einfahrgeschwindigkeit, denn dort wirkt sie', () => {
50 const werte = bezugswerte('knotenpunkt', KFZ_GERADEAUS, 'kfz', RILSA_DEFAULTS);
51 expect(werte.einfahrgeschwindigkeit).toBe(RILSA_DEFAULTS.enteringSpeed.kfz);
52 });
53
54 it('nennt die Ueberfahrzeit der Fahrbeziehung', () => {
55 const werte = bezugswerte('knotenpunkt', KFZ_GERADEAUS, 'kfz', RILSA_DEFAULTS);
56 expect(werte.ueberfahrzeit).toBeGreaterThan(0);
57 expect(werte.ueberfahrzeit).not.toBe(RILSA_ENGSTELLE.ueberfahrzeit);
58 });
59 });
60
61 describe('An der Engstelle gelten die Werte des Abschnitts 5.2', () => {
62 const werte = bezugswerte('engstelle', KFZ_GERADEAUS, 'kfz', RILSA_DEFAULTS);
63
64 it('nennt die Raeumgeschwindigkeit der Staffel und nicht den Vorgabenwert', () => {
65 // Genau der gemeldete Fall: 50 km/h zulaessig ergeben 40 km/h = 11,11 m/s,
66 // nicht die 10 m/s der Knotenpunktrechnung.
67 expect(werte.raeumgeschwindigkeit).toBeCloseTo(40 / 3.6, 6);
68 expect(werte.raeumgeschwindigkeit).not.toBe(RILSA_DEFAULTS.clearingSpeed.kfzStraight);
69 });
70
71 it('holt diese Zahl aus derselben Stelle wie die Rechnung', () => {
72 // Keine zweite Fassung der Staffel: Beide Wege muessen dieselbe Zahl geben.
73 expect(werte.raeumgeschwindigkeit).toBe(engstellenRaeumgeschwindigkeitMs(50));
74 });
75
76 it('nennt die feste Ueberfahrzeit von 4 s', () => {
77 expect(werte.ueberfahrzeit).toBe(RILSA_ENGSTELLE.ueberfahrzeit);
78 });
79
80 it('nennt gar keine Einfahrgeschwindigkeit, weil dort keine wirkt', () => {
81 // null und nicht 0: 0 waere eine Zahl, gegen die sich abwaegen liesse.
82 expect(werte.einfahrgeschwindigkeit).toBeNull();
83 });
84
85 it('gibt das Verfahren zurueck, nach dem es gerechnet hat', () => {
86 expect(werte.verfahren).toBe('engstelle');
87 });
88 });
89
90 describe('Die Staffel folgt der zulaessigen Hoechstgeschwindigkeit', () => {
91 it.each([
92 // Die Staffel liegt eine Stufe unter der zulaessigen Hoechstgeschwindigkeit,
93 // und unterhalb ihrer kleinsten Stufe gilt diese.
94 [30, 30],
95 [40, 30],
96 [50, 40],
97 [60, 50],
98 ])('bei %s km/h zulaessig steht der Bezugswert bei %s km/h', (vZul, erwartetKmh) => {
99 // Die Bezugsgroesse darf nicht an einer Zahl kleben: Sie haengt an der
100 // Oertlichkeit, und das Fenster muss die des jeweiligen Stroms nennen.
101 const werte = bezugswerte('engstelle', { ...KFZ_GERADEAUS, vZul }, 'kfz', RILSA_DEFAULTS);
102 expect(werte.raeumgeschwindigkeit).toBeCloseTo(erwartetKmh / 3.6, 6);
103 });
104 });
105
106 /*
107 * NACHTRAG (Fassung 5.43.0): Ein Feld desselben Fensters blieb bei
108 * der Gewohnheit.
109 *
110 * Die Zeile "Ueberfahrzeit tue" des Rechenwegs holt ihre Erlaeuterung aus
111 * `ueberfahrzeitHerkunft`, und die Funktion kannte nur die Verkehrsart und den
112 * gewaehlten Kfz-Ansatz. An einer Engstelle stand deshalb neben den 4 s des
113 * Abschnitts 5.2.2 die Beschriftung des KNOTENPUNKT-Ansatzes - bei der Vorgabe
114 * "Überfahrzeit = Gelbzeit" also "3/4/5 s nach zulässiger
115 * Hoechstgeschwindigkeit" und beim festen Ansatz "3 s geradeaus / 2 s
116 * abbiegend", beides neben einer Zahl, die keiner der beiden hergibt. Dieselbe
117 * Spalte druckt die Tabellenausgabe (services/export/csv.ts).
118 *
119 * Die Funktion bekommt das Verfahren jetzt als ERSTES Argument - wie
120 * `bezugswerte` darueber und aus demselben Grund: So muss jede Aufrufstelle es
121 * mitgeben, und eine vergessene bricht beim Uebersetzen statt in der Ausgabe.
122 */
123 describe('Die Herkunft der Ueberfahrzeit kennt das Verfahren ebenso', () => {
124 it('nennt an der Engstelle den Regelwert des Abschnitts 5.2.2 und nicht den Kfz-Ansatz', () => {
125 for (const ansatz of ['gelbzeit', 'fest'] as const) {
126 const text = ueberfahrzeitHerkunft('engstelle', 'kfz', ansatz);
127 expect(text, ansatz).toBe(
128 `Regelwert der Engstelle: tü = ${RILSA_ENGSTELLE.ueberfahrzeit} s ` +
129 '(RiLSA 2015, Abschnitt 5.2.2)',
130 );
131 expect(text, `${ansatz}: Beschriftung des Kfz-Ansatzes`).not.toContain(
132 UEBERFAHRZEIT_ANSATZ_LABELS[ansatz],
133 );
134 }
135 });
136
137 it('sagt an der Engstelle, dass eine eingetragene Ueberfahrzeit nicht eingeht', () => {
138 /*
139 * `computeEngstellenIntergreen` liest `crossingTimeOverride` gar nicht: Die
140 * Ueberfahrzeit ist dort die feste Zahl des Abschnitts. Bis hierher hiess
141 * eine Eingabe von genau 4 s "an dieser Beziehung von Hand eingetragen" -
142 * eine Herleitung, die nicht stattgefunden hat -, und eine von 6 s
143 * "verworfen", als haette eine Pruefung sie abgelehnt.
144 */
145 for (const vorgabewert of [4, 6]) {
146 expect(
147 ueberfahrzeitHerkunft('engstelle', 'kfz', 'gelbzeit', {
148 vorgegeben: true,
149 vorgabewert,
150 angesetzt: RILSA_ENGSTELLE.ueberfahrzeit,
151 }),
152 `Vorgabe ${vorgabewert} s`,
153 ).toBe(
154 `Regelwert der Engstelle: tü = ${RILSA_ENGSTELLE.ueberfahrzeit} s ` +
155 `(RiLSA 2015, Abschnitt 5.2.2) – die an dieser Beziehung eingetragenen ${vorgabewert} s ` +
156 'gehen in dieses Verfahren nicht ein',
157 );
158 }
159 });
160
161 it('laesst am Knotenpunkt jede Antwort, wie sie war', () => {
162 // Die Gegenprobe: Der Zweig darf nur an der Engstelle greifen.
163 for (const ansatz of ['gelbzeit', 'fest'] as const) {
164 expect(ueberfahrzeitHerkunft('knotenpunkt', 'kfz', ansatz)).toBe(
165 UEBERFAHRZEIT_ANSATZ_LABELS[ansatz],
166 );
167 }
168 expect(
169 ueberfahrzeitHerkunft('knotenpunkt', 'kfz', 'gelbzeit', {
170 vorgegeben: true,
171 vorgabewert: 4,
172 angesetzt: 4,
173 }),
174 ).toBe('an dieser Beziehung von Hand eingetragen');
175 expect(ueberfahrzeitHerkunft('knotenpunkt', 'fuss', 'fest')).toBe(
176 'Regelwert der Fußgänger aus den Vorgaben',
177 );
178 });
179
180 it('holt die 4 s aus derselben Stelle wie die Rechnung', () => {
181 // Keine zweite Fassung des Kennwerts: constants.ts ist die einzige Quelle.
182 const text = ueberfahrzeitHerkunft('engstelle', 'kfz', 'gelbzeit');
183 expect(text).toContain(`${RILSA_ENGSTELLE.ueberfahrzeit} s`);
184 expect(bezugswerte('engstelle', KFZ_GERADEAUS, 'kfz', RILSA_DEFAULTS).ueberfahrzeit).toBe(
185 RILSA_ENGSTELLE.ueberfahrzeit,
186 );
187 });
188 });