lsa-planer
LSA-Planer Professional – Planungssoftware für Lichtsignalanlagen nach RiLSA 2015 und § 45 StVO. EUPL-1.2.
/ src services export bewertung.ts
| 1 | import { |
| 2 | BEWERTUNGSVERFAHREN_LABELS, |
| 3 | ENGER_RADIUS_LABEL, |
| 4 | ENGSTELLE_PRAXIS, |
| 5 | HBS_GEOMETRIE, |
| 6 | HBS_LASTZUGANTEIL_BEI_UNBEKANNTER_AUFTEILUNG, |
| 7 | HBS_PKW_GLEICHWERTE, |
| 8 | HBS_STANDARDBEDINGUNGEN, |
| 9 | KRITERIUM_LABELS, |
| 10 | RILSA_DEFAULTS, |
| 11 | RSA_EINSTREIFIG, |
| 12 | SAFETY_FLOORS, |
| 13 | SERVICE_LEVEL_SCALES, |
| 14 | STAURAUMBEDARF_JE_FAHRZEUG, |
| 15 | UEBERFAHRZEIT_ANSATZ_LABELS, |
| 16 | VZUL_OBERGRENZE_LSA, |
| 17 | type RilsaDefaults, |
| 18 | type ServiceLevelStep, |
| 19 | } from '@/domain/rilsa/constants'; |
| 20 | import { QUELLEN, type Quelle, type QuellenSchluessel } from '@/domain/rilsa/quellen'; |
| 21 | import { ueberfahrzeitAnsatzRichtungSatz, ueberfahrzeitVerkuerzung } from '@/domain/rilsa/ansaetze'; |
| 22 | import { |
| 23 | aufteilungAusLastzuganteil, |
| 24 | istLastzuganteil, |
| 25 | lastzuganteilSpanne, |
| 26 | schwerverkehrsfaktor, |
| 27 | wirksamerSchwerverkehrsanteil, |
| 28 | } from '@/domain/rilsa/capacity'; |
| 29 | import { hatSaettigungsverkehrsstaerke } from '@/domain/plan/signalPlan'; |
| 30 | import { |
| 31 | resolveRilsaSettings, |
| 32 | settingFields, |
| 33 | wirkungslosigkeit, |
| 34 | type RilsaOverrides, |
| 35 | type SettingDeviation, |
| 36 | type WirkungsKontext, |
| 37 | } from '@/domain/rilsa/settings'; |
| 38 | import type { |
| 39 | Anlagenart, |
| 40 | Bewertungsverfahren, |
| 41 | ServiceLevel, |
| 42 | UeberfahrzeitAnsatz, |
| 43 | } from '@/domain/rilsa/types'; |
| 44 | import type { Project, TrafficDemand } from '@/domain/model/project'; |
| 45 | import { roundTo, type Ratio } from '@/domain/units'; |
| 46 | import * as fmt from '@/ui/format'; |
| 47 | |
| 48 | /** |
| 49 | * Benennung von Bewertungsverfahren, Kriterium und Stufe fuer Tabelle und |
| 50 | * Ausdruck. |
| 51 | * |
| 52 | * Steht hier und nicht in pdf.ts, weil beide Ausgabewege dieselben Worte |
| 53 | * fuehren muessen (Vorbild: wegherkunft.ts). Vor Fassung 5.4.0 |
| 54 | * (Befunde B1 bis B4) schrieb der Ausdruck "Bewertung nach HBS 2015", die |
| 55 | * Tabelle nur "HBS" - und beides fuer eine Rechnung, die in Wahrheit die |
| 56 | * HCM-Formel war. Jetzt gibt es zwei Verfahren, die auch rechnen, was sie |
| 57 | * behaupten; die Benennung darf deshalb nirgends mehr frei formuliert werden. |
| 58 | * |
| 59 | * Die Woerter selbst stehen im Fachkern (constants.ts: |
| 60 | * BEWERTUNGSVERFAHREN_LABELS, KRITERIUM_LABELS) und werden hier nur |
| 61 | * durchgereicht: Vor der Fassung 5.4.0 fuehrte diese Datei eine zweite, eigene |
| 62 | * Tafel derselben Woerter - zwei Quellen fuer dieselbe Benennung sind genau die |
| 63 | * Stelle, an der Bildschirm und Ausdruck unbemerkt auseinanderlaufen. |
| 64 | */ |
| 65 | |
| 66 | export { KRITERIUM_LABELS }; |
| 67 | |
| 68 | /** Name des Bewertungsverfahrens, wie er in Ausdruck und Tabelle steht. */ |
| 69 | export function verfahrenLabel(verfahren: Bewertungsverfahren): string { |
| 70 | return BEWERTUNGSVERFAHREN_LABELS[verfahren]; |
| 71 | } |
| 72 | |
| 73 | /** |
| 74 | * Stufe fuer eine Tabellenzelle. Bei Ueberlastung steht der Grund dabei: Nach |
| 75 | * HBS 2015 ist F fuer Kfz keine Wartezeitstufe, sondern gilt genau dann, wenn |
| 76 | * q > C - ohne den Zusatz liesse sich das "F" neben einer Wartezeit von 40 s |
| 77 | * nicht mit der gedruckten Stufentafel in Einklang bringen. |
| 78 | */ |
| 79 | export function stufeZelle(level: ServiceLevel | null, leer = '–'): string { |
| 80 | if (level === null) return leer; |
| 81 | return level.kriterium === 'ueberlastung' ? `${level.grade} (überlastet)` : level.grade; |
| 82 | } |
| 83 | |
| 84 | /** |
| 85 | * Stufentafel als Satzteil, aus denselben Konstanten, mit denen bewertet |
| 86 | * wird: "A bis 20 s, B bis 35 s, C bis 50 s, D bis 70 s, E darüber". |
| 87 | * |
| 88 | * Nicht ausgeschrieben, weil ausgeschriebene Zahlen die Stelle sind, an der |
| 89 | * ein Ausdruck unbemerkt veraltet: Vor Fassung 5.4.0 (Befund B1) |
| 90 | * stand die Kfz-Tafel des HBS 2001 unter der Ueberschrift "HBS 2015". |
| 91 | */ |
| 92 | export function stufentafelSatz(tafel: readonly ServiceLevelStep[]): string { |
| 93 | return tafel |
| 94 | .map((stufe) => |
| 95 | Number.isFinite(stufe.maxDelay) |
| 96 | ? `${stufe.grade} bis ${fmt.seconds(stufe.maxDelay)}` |
| 97 | : `${stufe.grade} darüber`, |
| 98 | ) |
| 99 | .join(', '); |
| 100 | } |
| 101 | |
| 102 | /** Die Tafeln des Verfahrens, benannt fuer den Ausdruck. */ |
| 103 | export function stufentafeln(verfahren: Bewertungsverfahren): { |
| 104 | readonly kfz: string; |
| 105 | readonly oepnv: string; |
| 106 | readonly fussRad: string | null; |
| 107 | } { |
| 108 | if (verfahren === 'HBS') { |
| 109 | return { |
| 110 | kfz: stufentafelSatz(SERVICE_LEVEL_SCALES.HBS.kfz), |
| 111 | oepnv: stufentafelSatz(SERVICE_LEVEL_SCALES.HBS.oepnv), |
| 112 | fussRad: stufentafelSatz(SERVICE_LEVEL_SCALES.HBS.fussRad), |
| 113 | }; |
| 114 | } |
| 115 | const kfz = stufentafelSatz(SERVICE_LEVEL_SCALES.HCM.kfz); |
| 116 | // Das HCM-Verfahren dieses Programms hat fuer Fussgaenger und Radverkehr |
| 117 | // keine Tafel (delay.ts, serviceLevelFor liefert null). |
| 118 | return { kfz, oepnv: kfz, fussRad: null }; |
| 119 | } |
| 120 | |
| 121 | /** |
| 122 | * Herkunft, an der kein Vorgabenfeld haengt. |
| 123 | * |
| 124 | * Das Fundstellenverzeichnis (Ausdruck und Vorgabenverwaltung) entsteht aus |
| 125 | * settingFieldsByQuelle - es kennt also nur Quellen, zu denen ein anpassbarer |
| 126 | * Kennwert gehoert. Die Stufentafeln der Qualitaetsstufen sind kein solcher |
| 127 | * Kennwert: Sie sind fest hinterlegt und nicht einstellbar. Die Quelle |
| 128 | * 'hbs-qualitaetsstufen' erschien deshalb vor der Fassung 5.4.0 NIRGENDS: Der |
| 129 | * Ausdruck druckte die Tafeln im Leistungsabschnitt, aber im Nachweis der |
| 130 | * Kennwerte hatten sie weder Herkunft noch Pruefstand - und gerade die OePNV- |
| 131 | * und Fuss/Rad-Tafeln sind nur ueber Sekundaerquellen belegt und am Original |
| 132 | * abzugleichen. |
| 133 | * |
| 134 | * Diese Liste ergaenzt das Verzeichnis um solche Quellen. Sie steht hier und |
| 135 | * nicht in settings.ts, weil sie keine Vorgaben beschreibt, sondern Kennwerte, |
| 136 | * die nur ausgegeben werden; kommt eine weitere feldlose Quelle hinzu, bekommt |
| 137 | * sie hier einen Eintrag und erscheint damit in beiden Verzeichnissen. |
| 138 | */ |
| 139 | export interface QuelleOhneFeld { |
| 140 | readonly schluessel: QuellenSchluessel; |
| 141 | readonly quelle: Quelle; |
| 142 | /** Was die Quelle beisteuert - an Stelle der Kennwertliste einer Vorgabengruppe. */ |
| 143 | readonly bezeichnung: string; |
| 144 | /** |
| 145 | * Die Werte, mit denen TATSAECHLICH gerechnet wird, aus den Konstanten des |
| 146 | * Fachkerns - nicht ausgeschrieben, aus demselben Grund wie in stufentafelSatz. |
| 147 | */ |
| 148 | readonly kennwerte: readonly { readonly label: string; readonly wert: string }[]; |
| 149 | } |
| 150 | |
| 151 | export function quellenOhneFeld( |
| 152 | verfahren: Bewertungsverfahren = 'HBS', |
| 153 | anlagenart: Anlagenart = 'knotenpunkt', |
| 154 | // Die Kennwerte, mit denen gerechnet wird - nicht die Regelwerte. Siehe |
| 155 | // vwvStvoEintrag (Befund C21 im gedruckten Nachweis). |
| 156 | defaults: RilsaDefaults = RILSA_DEFAULTS, |
| 157 | /** |
| 158 | * Wird der Verfahrensvergleich in DIESER Unterlage gedruckt? |
| 159 | * |
| 160 | * Nur dann gehoert seine Regel in das Verzeichnis. Dieselbe Ueberlegung wie |
| 161 | * bei der RSA 21: Was in keine gedruckte Zahl eingeht, gehoert nicht in den |
| 162 | * Nachweis dieses Projekts - ein Verzeichnis, das alles auffuehrt, sagt |
| 163 | * nichts darueber, worauf diese Unterlage beruht. |
| 164 | * |
| 165 | * Die Vorgabenansicht ruft ohne dieses Kennzeichen: Dort steht die Herkunft |
| 166 | * der VORGABEN, und die Vergleichsregel ist keine. |
| 167 | */ |
| 168 | mitVerfahrensvergleich = false, |
| 169 | ): readonly QuelleOhneFeld[] { |
| 170 | const eintraege: QuelleOhneFeld[] = [stufentafelnEintrag(verfahren)]; |
| 171 | |
| 172 | /* |
| 173 | * DIE REGEL DES VERFAHRENSVERGLEICHS. |
| 174 | * |
| 175 | * Sie stand frueher unbelegt im Quelltext, und das fiel nicht auf, weil |
| 176 | * niemand sie sah: Der Vergleich hatte keinen Aufrufer. Seit sein Ergebnis |
| 177 | * in der Planunterlage steht, gilt derselbe Satz wie fuer jede andere Zahl |
| 178 | * darin - die Unterlage sagt, woher sie kommt. |
| 179 | */ |
| 180 | if (mitVerfahrensvergleich) eintraege.push(verfahrensvergleichEintrag()); |
| 181 | |
| 182 | /* |
| 183 | * VwV-StVO und RSA 21 (Befunde C2, C3, C5, C6): Beide bekamen einen Eintrag |
| 184 | * in QUELLEN, aber zunaechst keinen Weg ins Verzeichnis - das kennt nur |
| 185 | * Vorgabenfelder und diese Liste, und an beiden haengt kein Vorgabenfeld. Die |
| 186 | * Unterlagen behaupteten "neuer Eintrag VwV-StVO im Fundstellenverzeichnis", |
| 187 | * und im Ausdruck stand er nicht. Die VwV-StVO gilt fuer jede Anlagenart; die |
| 188 | * RSA nur bei einstreifiger Verkehrsfuehrung - an einem Knotenpunkt geht |
| 189 | * keiner ihrer Anhaltswerte in eine Meldung ein und gehoert deshalb nicht in |
| 190 | * den Nachweis dieses Projekts. |
| 191 | */ |
| 192 | eintraege.push(vwvStvoEintrag(defaults)); |
| 193 | if (anlagenart === 'einstreifig') eintraege.push(rsaEintrag()); |
| 194 | |
| 195 | /* |
| 196 | * DIE GEOMETRISCHEN ANPASSUNGSFAKTOREN. Sie stehen zuletzt, weil sie als |
| 197 | * einzige der Liste jede Anlagenart und jedes Bewertungsverfahren betreffen: |
| 198 | * Sie wirken auf jede Saettigungsverkehrsstaerke - auch dort, wo keine |
| 199 | * Zufahrt vermessen ist, denn dann steht der Faktor 1 fuer eine ANGENOMMENE |
| 200 | * Standardbedingung und nicht fuer eine gemessene. Genau das gehoert in den |
| 201 | * Nachweis: Wer die Unterlage prueft, muss erfahren, dass die 2000 Kfz/h eine |
| 202 | * Voraussetzung haben. |
| 203 | */ |
| 204 | eintraege.push(geometriefaktorenEintrag()); |
| 205 | |
| 206 | /* |
| 207 | * DER STAURAUMBEDARF JE FAHRZEUG. Seit der Fassung 5.31.0 druckt die |
| 208 | * Unterlage eine Rueckstaulaenge, und die entsteht aus einem Praxiswert, den |
| 209 | * kein Regelwerk nennt. Eine Laenge ohne Herkunft in einer |
| 210 | * Anordnungsunterlage ist genau das, was dieses Verzeichnis verhindern soll. |
| 211 | */ |
| 212 | eintraege.push(stauraumbedarfEintrag()); |
| 213 | return eintraege; |
| 214 | } |
| 215 | |
| 216 | /** Die Regel, nach der der Verfahrensvergleich einen Wert benennt. */ |
| 217 | function verfahrensvergleichEintrag(): QuelleOhneFeld { |
| 218 | return { |
| 219 | schluessel: 'programm-umlaufzeitvergleich', |
| 220 | quelle: QUELLEN['programm-umlaufzeitvergleich'], |
| 221 | bezeichnung: 'Regel des Verfahrensvergleichs (keine Empfehlung, kein Regelwerkswert)', |
| 222 | kennwerte: [ |
| 223 | { |
| 224 | label: 'Maßgebender Wert', |
| 225 | wert: |
| 226 | 'der größere aus kapazitätsorientiertem Bedarf (HBS bzw. HCM, der größere) und ' + |
| 227 | 'Wartezeitoptimum (Webster bzw. Akçelik, der kleinere)', |
| 228 | }, |
| 229 | { |
| 230 | label: 'Wirkung auf den Signalzeitenplan', |
| 231 | wert: 'keine – gerechnet wird ausschließlich das unter „Phasen" gewählte Verfahren', |
| 232 | }, |
| 233 | ], |
| 234 | }; |
| 235 | } |
| 236 | |
| 237 | /** Die Laenge, die ein wartendes Fahrzeug im Rueckstau belegt. */ |
| 238 | function stauraumbedarfEintrag(): QuelleOhneFeld { |
| 239 | return { |
| 240 | schluessel: 'praxis-stauraum-je-fahrzeug', |
| 241 | quelle: QUELLEN['praxis-stauraum-je-fahrzeug'], |
| 242 | bezeichnung: 'Rückstaulänge aus dem mittleren Rückstau bei Freigabezeitende', |
| 243 | kennwerte: [ |
| 244 | { |
| 245 | label: 'Länge je wartendem Fahrzeug', |
| 246 | wert: `${fmt.meters(STAURAUMBEDARF_JE_FAHRZEUG, 0)} (Fahrzeug und Abstand)`, |
| 247 | }, |
| 248 | { |
| 249 | label: 'Verglichene Größe', |
| 250 | wert: |
| 251 | 'der MITTLERE Rückstau NGE über die Betrachtungsstunde – in etwa der Hälfte der ' + |
| 252 | 'Umläufe steht mehr; ein 95-Prozent-Wert wird nicht gebildet', |
| 253 | }, |
| 254 | ], |
| 255 | }; |
| 256 | } |
| 257 | |
| 258 | /** Die geometrischen Anpassungsfaktoren des Zeitbedarfswerts. */ |
| 259 | function geometriefaktorenEintrag(): QuelleOhneFeld { |
| 260 | const { fahrstreifenbreiteAb, kurvenradiusAb, laengsneigungBis } = HBS_STANDARDBEDINGUNGEN; |
| 261 | return { |
| 262 | schluessel: 'hbs-geometriefaktoren', |
| 263 | quelle: QUELLEN['hbs-geometriefaktoren'], |
| 264 | bezeichnung: 'Geometrie der Zufahrt (Fahrstreifenbreite, Kurvenradius, Längsneigung)', |
| 265 | kennwerte: [ |
| 266 | { |
| 267 | label: 'Standardbedingungen (Faktor 1)', |
| 268 | wert: |
| 269 | `Breite ab ${fmt.numShort(fahrstreifenbreiteAb, 2)} m, Radius ab ` + |
| 270 | `${fmt.numShort(kurvenradiusAb, 0)} m, Längsneigung zwischen ` + |
| 271 | `−${fmt.numShort(laengsneigungBis, 0)} % und ${fmt.numShort(laengsneigungBis, 0)} %`, |
| 272 | }, |
| 273 | { |
| 274 | label: 'Anpassungsfaktoren', |
| 275 | wert: |
| 276 | `fb = ${fmt.numShort(HBS_GEOMETRIE.breite.steigung, 3)} · b + ` + |
| 277 | `${fmt.numShort(HBS_GEOMETRIE.breite.achsenabschnitt, 3)}; ` + |
| 278 | `fR = ${fmt.numShort(HBS_GEOMETRIE.radius.steigung, 3)} · R + ` + |
| 279 | `${fmt.numShort(HBS_GEOMETRIE.radius.achsenabschnitt, 1)}; ` + |
| 280 | `fs = ${fmt.numShort(HBS_GEOMETRIE.neigung.steigung, 2)} · s + 1; ` + |
| 281 | 'angesetzt wird max(1; max(fb, fR, fs) · min(1; fs))', |
| 282 | }, |
| 283 | ], |
| 284 | }; |
| 285 | } |
| 286 | |
| 287 | /** Stufentafeln der Qualitaetsstufen des gewaehlten Verfahrens. */ |
| 288 | function stufentafelnEintrag(verfahren: Bewertungsverfahren): QuelleOhneFeld { |
| 289 | const mittlere = KRITERIUM_LABELS['mittlere-wartezeit']; |
| 290 | const maximale = KRITERIUM_LABELS['maximale-wartezeit']; |
| 291 | // Nach HBS 2015 ist F fuer Kfz keine Wartezeitstufe (E ist nach oben offen), |
| 292 | // fuer OePNV eine Wartezeitstufe UND die Folge der Ueberlastung; die Tafel |
| 293 | // allein liesse das F der Leistungstabelle unerklaert. |
| 294 | const fUeberlastung = `F bei ${KRITERIUM_LABELS.ueberlastung}`; |
| 295 | if (verfahren === 'HCM') { |
| 296 | // Im HCM-Verfahren wird nach der HCM-Tafel bewertet; die HBS-Tafeln |
| 297 | // gehen in keine Zahl ein und gehoeren deshalb nicht in den Nachweis |
| 298 | // dieses Projekts. |
| 299 | return { |
| 300 | schluessel: 'hcm-verfahren', |
| 301 | quelle: QUELLEN['hcm-verfahren'], |
| 302 | bezeichnung: `Level of Service (Kfz/ÖPNV: ${mittlere}; Fuß/Rad: keine Tafel)`, |
| 303 | kennwerte: [ |
| 304 | { |
| 305 | label: `Stufentafel Kfz und ÖPNV (${mittlere})`, |
| 306 | wert: `${stufentafelSatz(SERVICE_LEVEL_SCALES.HCM.kfz)}; ${fUeberlastung}`, |
| 307 | }, |
| 308 | ], |
| 309 | }; |
| 310 | } |
| 311 | return { |
| 312 | schluessel: 'hbs-qualitaetsstufen', |
| 313 | quelle: QUELLEN['hbs-qualitaetsstufen'], |
| 314 | bezeichnung: `Stufentafeln der Qualitätsstufen (Kfz/ÖPNV: ${mittlere}; Fuß/Rad: ${maximale})`, |
| 315 | kennwerte: [ |
| 316 | { |
| 317 | label: `Stufentafel Kfz (${mittlere})`, |
| 318 | wert: `${stufentafelSatz(SERVICE_LEVEL_SCALES.HBS.kfz)}; ${fUeberlastung}`, |
| 319 | }, |
| 320 | { |
| 321 | label: `Stufentafel ÖPNV (${mittlere})`, |
| 322 | wert: `${stufentafelSatz(SERVICE_LEVEL_SCALES.HBS.oepnv)}; ${fUeberlastung}`, |
| 323 | }, |
| 324 | { |
| 325 | label: `Stufentafel Fußgänger und Radverkehr (${maximale})`, |
| 326 | wert: stufentafelSatz(SERVICE_LEVEL_SCALES.HBS.fussRad), |
| 327 | }, |
| 328 | ], |
| 329 | }; |
| 330 | } |
| 331 | |
| 332 | /** |
| 333 | * Gelbzeitstaffel als Satzteil aus den Vorgaben, mit denen gerechnet wird: |
| 334 | * "3 s bis 50 km/h, 4 s bis 60 km/h, 5 s bis 70 km/h". Die letzte Stufe der |
| 335 | * RiLSA ist nach oben offen; hier endet sie bei der Hoechstgeschwindigkeit, |
| 336 | * bis zu der Lichtsignalanlagen eingerichtet werden sollen - darueber gilt im |
| 337 | * Programm dieselbe Gelbzeit, aber die VwV-StVO sieht dort keine Anlage vor. |
| 338 | * |
| 339 | * KORREKTUR (Befund C21): Genau dieser Kommentar stand schon hier, der |
| 340 | * Quelltext nahm aber RILSA_DEFAULTS - der Kommentar sagte das Gegenteil des |
| 341 | * Codes. Die Felder `yellowKfzUpTo50/UpTo60/Above60` sind einstellbar; wer sie |
| 342 | * aendert, bekam im Fundstellenverzeichnis der Planunterlage weiter die |
| 343 | * Regelstaffel gedruckt. |
| 344 | * |
| 345 | * WARUM EXPORTIERT: Derselbe Satz steht als Feldhinweis am vZul-Feld |
| 346 | * (projectView). Zwei Stellen, die dieselbe Staffel unabhaengig voneinander |
| 347 | * zusammensetzen, laufen genau dann auseinander, wenn niemand daran denkt. |
| 348 | */ |
| 349 | export function gelbzeitstaffelSatz(defaults: RilsaDefaults = RILSA_DEFAULTS): string { |
| 350 | return defaults.yellowBySpeed |
| 351 | .map((stufe) => |
| 352 | Number.isFinite(stufe.upToKmh) |
| 353 | ? `${fmt.seconds(stufe.yellow)} bis ${fmt.numShort(stufe.upToKmh)} km/h` |
| 354 | : `${fmt.seconds(stufe.yellow)} bis ${fmt.numShort(VZUL_OBERGRENZE_LSA)} km/h`, |
| 355 | ) |
| 356 | .join(', '); |
| 357 | } |
| 358 | |
| 359 | /** |
| 360 | * Welcher Ueberfahrzeit-Ansatz gerechnet wurde - und was daran haengt |
| 361 | * (Fassung 5.5.0, Frage E2). |
| 362 | * |
| 363 | * Der Ansatz aendert JEDE Zwischenzeit eines Kfz-Raeumstroms. Eine |
| 364 | * Zwischenzeitenmatrix ohne diese Angabe laesst sich nicht nachrechnen: Bei |
| 365 | * 60 km/h und abbiegendem Strom stehen je nach Ansatz 4 s oder 2 s |
| 366 | * Ueberfahrzeit hinter derselben Zahl. |
| 367 | * |
| 368 | * WARUM DER SATZ IN BEIDEN ZWEIGEN DIE RICHTUNG NENNT: Die feste Ueberfahrzeit |
| 369 | * VERKUERZT die Zwischenzeit gegenueber dem Regelansatz dieses Programms - bei |
| 370 | * den Regelwerten. Eine Verkuerzung, die in der Unterlage nur als Name eines |
| 371 | * Ansatzes erscheint, ist vor der Behoerde keine ausgewiesene Abweichung - sie |
| 372 | * sieht aus wie eine Einstellung. Deshalb stehen Richtung, Groessenordnung und |
| 373 | * Herkunft der Werte im selben Satz, und die NICHT eingebaute Untergrenze steht |
| 374 | * dabei: Wer sie braucht, muss wissen, dass dieses Programm sie nicht setzt. |
| 375 | * |
| 376 | * Die beiden Zahlen kommen aus den aufgeloesten Vorgaben und nicht aus dem |
| 377 | * Satz (Befund C21): Sie sind einstellbar, und ein Ausdruck, der 3 s und 2 s |
| 378 | * behauptet, waehrend die Rechnung mit anderen Werten lief, ist schlimmer als |
| 379 | * gar keine Angabe. |
| 380 | * |
| 381 | * NACHGEZOGEN (Fassung 5.10.0): Dieser Docblock begruendete nur den Zweig |
| 382 | * 'fest', und nur dort kamen Richtung und Groessenordnung aus dem Fachkern. Der |
| 383 | * Regelzweig - der in fast jeder Planunterlage steht - trug beides fest |
| 384 | * eingeschrieben im Text, obwohl die festen Ueberfahrzeiten auch bei ihm |
| 385 | * einstellbar sind und die Gelbzeitstaffel ebenso. Beide Zweige bilden Richtung |
| 386 | * und Herkunft jetzt aus den wirksamen Vorgaben (regelansatzVergleichSatz, |
| 387 | * festeWerteHerkunftSatz). |
| 388 | */ |
| 389 | export function ueberfahrzeitAnsatzSatz( |
| 390 | ansatz: UeberfahrzeitAnsatz, |
| 391 | defaults: RilsaDefaults = RILSA_DEFAULTS, |
| 392 | ): string { |
| 393 | const geradeaus = fmt.seconds(defaults.crossingTime.kfzGeradeaus); |
| 394 | const abbiegend = fmt.seconds(defaults.crossingTime.kfzAbbiegend); |
| 395 | const feste = `${geradeaus} geradeaus und ${abbiegend} abbiegend`; |
| 396 | |
| 397 | if (ansatz === 'gelbzeit') { |
| 398 | // EINE Antwort fuer beide Saetze: Der Herkunftssatz entscheidet, ob die |
| 399 | // genannten Zahlen Leitfaden- oder Vorgabewerte sind, der Vergleichssatz |
| 400 | // daneben nennt sie beim selben Namen. |
| 401 | const nachLeitfaden = festeWerteNachLeitfaden(defaults); |
| 402 | return ( |
| 403 | 'Überfahrzeit des Kraftfahrzeugverkehrs: Gerechnet wurde mit dem Ansatz ' + |
| 404 | `„${UEBERFAHRZEIT_ANSATZ_LABELS.gelbzeit}" – dem Regelansatz dieses Programms. Angesetzt ` + |
| 405 | `ist damit die Gelbzeit dieser Signalgruppe (${gelbzeitstaffelSatz(defaults)}). ` + |
| 406 | // Herkunft UND Richtung aus den wirksamen Vorgaben (Fassung 5.10.0) - wie |
| 407 | // im Zweig 'fest' eine Zeile weiter unten. Hier stand beides fest |
| 408 | // eingeschrieben im Satz. |
| 409 | `${festeWerteHerkunftSatz(feste, nachLeitfaden)} ` + |
| 410 | `${regelansatzVergleichSatz(defaults, nachLeitfaden)}` |
| 411 | ); |
| 412 | } |
| 413 | |
| 414 | /* |
| 415 | * "nie kürzer" und nicht mehr "länger" (Fassung 5.43.0): Bei 50 km/h |
| 416 | * geradeaus ergeben beide Ansaetze 3 s. Wahr bei jeder zulaessigen |
| 417 | * Gelbzeitstaffel ist nur "nie kürzer", und auch das nur gegen die Regelwerte |
| 418 | * der RiLSA: SAFETY_FLOORS.yellowKfz liegt nicht unter ihnen, wohl aber unter |
| 419 | * jeder festen Vorgabe ueber 3 s. |
| 420 | * |
| 421 | * NACHGEZOGEN (Fassung 5.43.0): Hier stand der Satz unbedingt - |
| 422 | * "Die Werte sind die Regelwerte der RiLSA 2015 ...; ... rechnet nie kürzer |
| 423 | * als diese" auch bei 4 s abbiegend, wo die Gelbzeit bei 50 km/h 3 s |
| 424 | * betraegt, und bei 6 s / 6 s unmittelbar hinter dem Satz, die Werte wichen |
| 425 | * von den Regelwerten ab. Beide Aussagen haengen jetzt an derselben Antwort |
| 426 | * wie im Zweig 'gelbzeit' (festeWerteNachLeitfaden). |
| 427 | */ |
| 428 | const nachLeitfaden = festeWerteNachLeitfaden(defaults); |
| 429 | const zuschreibung = nachLeitfaden |
| 430 | ? 'Die Werte sind die Regelwerte der RiLSA 2015 (Abschnitt 2.5.2, Fälle 1 und 2); der ' + |
| 431 | 'Regelansatz dieses Programms – die Kopplung an die Gelbzeit – rechnet nie kürzer als diese.' |
| 432 | : `Die Werte sind Vorgaben dieses Plans, nicht die ${rilsaFesteWerte()} der RiLSA 2015 ` + |
| 433 | '(Abschnitt 2.5.2, Fälle 1 und 2); der Regelansatz dieses Programms – die Kopplung an die ' + |
| 434 | 'Gelbzeit – rechnet nie kürzer als die Regelwerte der RiLSA 2015.'; |
| 435 | return ( |
| 436 | 'Überfahrzeit des Kraftfahrzeugverkehrs: Gerechnet wurde mit dem Ansatz ' + |
| 437 | `„${UEBERFAHRZEIT_ANSATZ_LABELS.fest}" – angesetzt sind ${feste}, unabhängig von der ` + |
| 438 | // Richtung UND Groessenordnung aus den wirksamen Vorgaben: Beide festen |
| 439 | // Ueberfahrzeiten sind einstellbar, "bis zu 3 s" galt nur fuer ihre |
| 440 | // Regelwerte. |
| 441 | `zulässigen Höchstgeschwindigkeit. ${ueberfahrzeitAnsatzRichtungSatz(defaults)} ` + |
| 442 | `${zuschreibung} Die Untergrenze der Überfahr- und Räumzeit (tü + tr mindestens ` + |
| 443 | 'Gelbzeit + 1 s, RiLSA 2015, Abschnitt 2.5.2) ' + |
| 444 | 'gilt bei beiden Ansätzen und ist eingebaut; wo sie greift, nennt der Rechenweg sie.' |
| 445 | ); |
| 446 | } |
| 447 | |
| 448 | /** "3 s geradeaus und 2 s abbiegend" - die festen Ueberfahrzeiten aus constants.ts. */ |
| 449 | function rilsaFesteWerte(): string { |
| 450 | return ( |
| 451 | `${fmt.seconds(RILSA_DEFAULTS.crossingTime.kfzGeradeaus)} geradeaus und ` + |
| 452 | `${fmt.seconds(RILSA_DEFAULTS.crossingTime.kfzAbbiegend)} abbiegend` |
| 453 | ); |
| 454 | } |
| 455 | |
| 456 | /** |
| 457 | * Entsprechen die beiden festen Ueberfahrzeiten dieses Plans den Regelwerten |
| 458 | * aus constants.ts? |
| 459 | * |
| 460 | * Zwei Saetze haengen daran: die Zuschreibung an die RiLSA und das Bezugswort |
| 461 | * des Vergleichssatzes daneben. Sie muessen dieselbe Antwort bekommen - sonst |
| 462 | * nennt der eine "Vorgabewerte", was der andere gerade dem Regelwerk |
| 463 | * zugeschrieben hat. |
| 464 | * |
| 465 | * NACHGEZOGEN (5.27.0): Der Parametername `nachLeitfaden` und die Saetze |
| 466 | * sprachen von behoerdlichen Leitfaeden, weil die beiden Zahlen bis dahin nur |
| 467 | * dort belegt waren. Sie stehen in der RiLSA 2015 (Abschnitt 2.5.2, Faelle 1 |
| 468 | * und 2); der Leitfaden bleibt eine zweite, uebereinstimmende Quelle. |
| 469 | */ |
| 470 | function festeWerteNachLeitfaden(defaults: RilsaDefaults): boolean { |
| 471 | return ( |
| 472 | defaults.crossingTime.kfzGeradeaus === RILSA_DEFAULTS.crossingTime.kfzGeradeaus && |
| 473 | defaults.crossingTime.kfzAbbiegend === RILSA_DEFAULTS.crossingTime.kfzAbbiegend |
| 474 | ); |
| 475 | } |
| 476 | |
| 477 | /** |
| 478 | * Woher die daneben genannten festen Ueberfahrzeiten stammen. |
| 479 | * |
| 480 | * KORREKTUR (Fassung 5.10.0): Der Regelzweig schrieb die eingesetzten Zahlen |
| 481 | * unbedingt behoerdlichen Leitfaeden zu (seit 5.27.0: der RiLSA). Belegt sind |
| 482 | * ausschliesslich die Regelwerte aus constants.ts (RiLSA 2015, Abschnitt |
| 483 | * 2.5.2); die Felder crossingTimeKfzGeradeaus und crossingTimeKfzAbbiegend sind |
| 484 | * von 1 bis 10 s einstellbar und bleiben es auch beim Ansatz 'gelbzeit' - die |
| 485 | * Vorgabenverwaltung vermerkt dort nur die Wirkungslosigkeit. Ein Pruefer las |
| 486 | * damit "6 s geradeaus" als Leitfadenwert: eine falsche Tatsachenbehauptung |
| 487 | * ueber ein Regelwerk in der Planunterlage. |
| 488 | * |
| 489 | * Weicht der Plan ab, stehen beide Zahlenpaare da - das eingetragene und das |
| 490 | * belegte. Der Pruefer soll die Abweichung sehen, nicht nur ihr Ergebnis. |
| 491 | */ |
| 492 | function festeWerteHerkunftSatz(feste: string, nachLeitfaden: boolean): string { |
| 493 | if (nachLeitfaden) { |
| 494 | return `Die RiLSA 2015 setzt in Abschnitt 2.5.2 feste Werte an (${feste}).`; |
| 495 | } |
| 496 | |
| 497 | return ( |
| 498 | `Die Vorgaben dieses Plans setzen für den Ansatz „feste Überfahrzeit" daneben ${feste} an – ` + |
| 499 | `nicht die ${rilsaFesteWerte()} der RiLSA 2015 (Abschnitt 2.5.2).` |
| 500 | ); |
| 501 | } |
| 502 | |
| 503 | /** |
| 504 | * Wie der Regelansatz gegen die daneben genannten festen Ueberfahrzeiten |
| 505 | * ausfaellt - aus den wirksamen Vorgaben gebildet, nicht aus dem Satz. |
| 506 | * |
| 507 | * KORREKTUR (Fassung 5.10.0): Hier stand "gegen sie faellt dieser Ansatz beim |
| 508 | * Abbiegen und bei 60 und 70 km/h laenger aus und ist damit die konservative |
| 509 | * Seite" - fest eingeschrieben, obwohl BEIDE Vergleichsseiten einstellbar sind: |
| 510 | * die festen Ueberfahrzeiten von 1 bis 10 s, die Gelbzeitstaffel ueber |
| 511 | * yellowKfzUpTo50/UpTo60/Above60. Mit 6 s geradeaus und 6 s abbiegend liegt die |
| 512 | * groesste Gelbzeit (5 s) darunter; der Satz behauptete die konservative Seite |
| 513 | * gegen zwei Zahlen, die er selbst nannte. Auch die Schwellen "60 und 70 km/h" |
| 514 | * galten nur fuer die Regelstaffel. Derselbe Fehlertyp ist fuer den Zweig |
| 515 | * 'fest' laengst behoben (ansaetze.ts, ueberfahrzeitAnsatzRichtungSatz); der |
| 516 | * Regelzweig war dabei uebersehen worden. |
| 517 | * |
| 518 | * Bezugsgroessen sind die KLEINSTE und die GROESSTE Gelbzeit der Staffel: Der |
| 519 | * Ansatz faellt dort am laengsten aus, wo die Gelbzeit am laengsten ist, und |
| 520 | * dort am kuerzesten, wo sie am kuerzesten ist. "Die konservative Seite" steht |
| 521 | * nur, wenn er NIRGENDS kuerzer ausfaellt - eine laengere Ueberfahrzeit heisst |
| 522 | * eine laengere Zwischenzeit (tz = tue + tr - te). |
| 523 | * |
| 524 | * DAS BEZUGSWORT STEHT AUSGESCHRIEBEN: Hier begann der Satz mit "Gegen sie" und |
| 525 | * haengte damit am vorangehenden Herkunftssatz. Weicht der Plan ab, nennt der |
| 526 | * zwei Zahlenpaare - die eingetragenen und die belegten -, und "sie" zeigte auf |
| 527 | * das zuletzt genannte, also auf die Leitfadenwerte, gegen die hier gar nicht |
| 528 | * gerechnet wird. Der Satz sagt jetzt allein stehend, welches Zahlenpaar der |
| 529 | * Massstab ist. |
| 530 | */ |
| 531 | function regelansatzVergleichSatz(defaults: RilsaDefaults, nachLeitfaden: boolean): string { |
| 532 | // Die laengere Haelfte kommt aus dem Fachkern: `ueberfahrzeitVerkuerzung` |
| 533 | // rechnet groesste Gelbzeit minus feste Ueberfahrzeit - dieselbe Groesse, die |
| 534 | // der Zweig 'fest' als Verkuerzung ausweist, hier aus der Gegenrichtung |
| 535 | // gelesen. Sie stand hier ein zweites Mal, samt eigenem Rueckfall bei leerer |
| 536 | // Gelbzeitstaffel. |
| 537 | const { abbiegend, geradeaus } = ueberfahrzeitVerkuerzung(defaults); |
| 538 | const laenger = vergleichsspanne(abbiegend, geradeaus); |
| 539 | |
| 540 | /* |
| 541 | * VORLAEUFIG AN DIESER STELLE: Die gespiegelte Groesse (feste Ueberfahrzeit |
| 542 | * minus KLEINSTE Gelbzeit) und der Satzbau dieses Zweigs gehoeren nach |
| 543 | * src/domain/rilsa/ansaetze.ts neben `ueberfahrzeitAnsatzRichtungSatz` - |
| 544 | * dort steht die Richtung der beiden Ansaetze an EINER Stelle, und der |
| 545 | * Bildschirm (projectView.ts) kann eine private Funktion dieser Datei nicht |
| 546 | * erreichen. ansaetze.ts stand fuer diese Aenderung nicht zur Verfuegung; |
| 547 | * solange die Verlegung aussteht, ist dies die zweite Fassung derselben |
| 548 | * Aussage - genau die Lage, gegen die der Docblock von ansaetze.ts steht. |
| 549 | */ |
| 550 | const kleinste = kleinsteGelbzeit(defaults); |
| 551 | const kuerzer = vergleichsspanne( |
| 552 | defaults.crossingTime.kfzAbbiegend - kleinste, |
| 553 | defaults.crossingTime.kfzGeradeaus - kleinste, |
| 554 | ); |
| 555 | |
| 556 | const bezug = nachLeitfaden ? 'Gegen diese Regelwerte' : 'Gegen diese Vorgabewerte'; |
| 557 | if (kuerzer === '') { |
| 558 | if (laenger === '') return `${bezug} fällt dieser Ansatz weder länger noch kürzer aus.`; |
| 559 | return ( |
| 560 | `${bezug} fällt dieser Ansatz ${laenger} länger aus und nirgends kürzer – er ist damit ` + |
| 561 | 'die konservative Seite.' |
| 562 | ); |
| 563 | } |
| 564 | return ( |
| 565 | `${bezug} fällt dieser Ansatz ${kuerzer} kürzer aus` + |
| 566 | (laenger === '' ? '' : ` und ${laenger} länger`) + |
| 567 | '; die konservative Seite ist er damit nicht.' |
| 568 | ); |
| 569 | } |
| 570 | |
| 571 | /** |
| 572 | * Kleinste Gelbzeit der Staffel - der Wert, gegen den der Regelansatz am |
| 573 | * kuerzesten ausfaellt. |
| 574 | * |
| 575 | * Gespiegelt zu `hoechsteGelbzeit` in ansaetze.ts, samt dessen Rueckfall auf |
| 576 | * die Regelstaffel: Eine leere Staffel darf hier keine Verkuerzung "um bis zu |
| 577 | * 6 s" behaupten. Gehoert mit der Funktion darueber in den Fachkern. |
| 578 | */ |
| 579 | function kleinsteGelbzeit(defaults: RilsaDefaults): number { |
| 580 | const werte = defaults.yellowBySpeed.map((stufe) => stufe.yellow); |
| 581 | if (werte.length > 0) return Math.min(...werte); |
| 582 | return Math.min(...RILSA_DEFAULTS.yellowBySpeed.map((stufe) => stufe.yellow)); |
| 583 | } |
| 584 | |
| 585 | /** |
| 586 | * "beim Abbiegen um bis zu 3 s und geradeaus um bis zu 2 s" - leer, wo in |
| 587 | * dieser Richtung nichts bleibt. Gerundet wird vor dem Vergleich, damit eine |
| 588 | * Differenz von 1e-15 keine Richtung behauptet. |
| 589 | * |
| 590 | * Zweite Fassung des Satzteils aus `ueberfahrzeitAnsatzRichtungSatz` |
| 591 | * (ansaetze.ts) und mit `fmt.numShort` statt dessen `formatZahl` gebildet - |
| 592 | * bei Ueberfahrzeiten von 1 bis 10 s liefern beide dieselbe Zeichenfolge. Auch |
| 593 | * sie gehoert in den Fachkern, siehe die Funktion darueber. |
| 594 | */ |
| 595 | function vergleichsspanne(abbiegend: number, geradeaus: number): string { |
| 596 | const teile: string[] = []; |
| 597 | if (roundTo(abbiegend, 2) > 0) { |
| 598 | teile.push(`beim Abbiegen um bis zu ${fmt.numShort(abbiegend, 2)} s`); |
| 599 | } |
| 600 | if (roundTo(geradeaus, 2) > 0) { |
| 601 | teile.push(`geradeaus um bis zu ${fmt.numShort(geradeaus, 2)} s`); |
| 602 | } |
| 603 | return teile.join(' und '); |
| 604 | } |
| 605 | |
| 606 | /** |
| 607 | * Wie das Merkmal "enger Innenradius" in einer Tabellenzelle steht - und |
| 608 | * warum es ueberhaupt eine eigene Spalte bekommt (Fassung 5.5.0, Frage E1). |
| 609 | * |
| 610 | * Das Merkmal steht an der einzelnen Konfliktbeziehung und senkt dort die |
| 611 | * Raeumgeschwindigkeit des abbiegenden Kraftfahrzeugs. In der gedruckten |
| 612 | * Zeile steht sonst nur die Zahl - 5,0 m/s neben einem Regelwert von 7,0 m/s, |
| 613 | * ohne erkennbaren Grund. Genau diese Luecke war der Befund hinter der Spalte |
| 614 | * "Schwerverkehr" der Leistungstabelle. |
| 615 | * |
| 616 | * ES STEHT DA, WAS ERFASST IST - NICHT, WAS GEWIRKT HAT: Die Zelle gibt das |
| 617 | * Merkmal der Beziehung wieder. Ob es die Rechnung geaendert hat, haengt am |
| 618 | * raeumenden Strom (nur abbiegende Kraftfahrzeuge); ist es ohne Wirkung |
| 619 | * gesetzt, meldet das der Pruefbericht als eigene Warnung |
| 620 | * ('zwischenzeiten.enger-radius-ohne-wirkung'). Die Ausgabe hier die Wirkung |
| 621 | * ein zweites Mal herleiten zu lassen, hiesse eine Regel des Fachkerns zu |
| 622 | * verdoppeln. |
| 623 | */ |
| 624 | export function engerRadiusZelle(gesetzt: boolean | undefined, leer = '–'): string { |
| 625 | return gesetzt === true ? ENGER_RADIUS_LABEL : leer; |
| 626 | } |
| 627 | |
| 628 | /** |
| 629 | * Was Ausdruck und Tabellenausgabe ueber den Schwerverkehr EINES Stroms sagen: |
| 630 | * der Faktor fSV, die Gleichung, die ihn gebildet hat, und die Datenlage, die |
| 631 | * ueber diese Gleichung entschieden hat. |
| 632 | * |
| 633 | * NEU (Schema 13). Bis dahin gab es hier nichts, weil es nichts zu entscheiden |
| 634 | * gab: Das Datenmodell kannte keine Aufteilung des Schwerverkehrs, jeder Strom |
| 635 | * lief ueber Gl. 2-6, und beide Ausgabewege bildeten den Faktor mit einem |
| 636 | * eigenen Aufruf `schwerverkehrsfaktor(anteil)`. Seit die Aufteilung erfassbar |
| 637 | * ist, ist genau das falsch: Der Plan rechnet mit Gl. 2-5, waehrend Ausdruck |
| 638 | * und Tabelle weiter den Pauschalwert drucken - bei 20 % Schwerverkehr aus |
| 639 | * lauter Lastzuegen stand dann fSV = 1,180 neben einer |
| 640 | * Saettigungsverkehrsstaerke von 1538 Fz/h, die zu 1,300 gehoert. Der |
| 641 | * Anpassungsfaktor wird deshalb hier gebildet, aus derselben Funktion und mit |
| 642 | * denselben Eingangsgroessen wie in plan/signalPlan.ts (groupSaturationFlow) - |
| 643 | * eine Stelle, zwei Ausgaben. |
| 644 | * |
| 645 | * DIE GLEICHUNG STEHT DABEI, NICHT NUR DER FAKTOR: Zwei Stroeme mit demselben |
| 646 | * Schwerverkehrsanteil koennen jetzt verschiedene Faktoren tragen, und das ist |
| 647 | * kein Fehler, sondern der Unterschied zwischen gezaehlt und nicht gezaehlt. |
| 648 | * Ohne die Angabe der Gleichung liesse sich die Unterlage nicht nachrechnen - |
| 649 | * der Pruefer kaeme mit dem Pauschalwert auf eine andere Zahl und wuesste |
| 650 | * nicht, warum. |
| 651 | */ |
| 652 | export interface Schwerverkehrsangabe { |
| 653 | /** Anpassungsfaktor fSV, mit dem der Plan diesen Strom rechnet. */ |
| 654 | readonly fsv: Ratio; |
| 655 | /** Rechnet dieser Strom nach Gl. 2-5 (erfasste Aufteilung)? */ |
| 656 | readonly mitAufteilung: boolean; |
| 657 | /** Kurzform fuer eine Tabellenzelle: "Gl. 2-5" bzw. "Gl. 2-6". */ |
| 658 | readonly gleichungKurz: string; |
| 659 | /** Die Datenlage in einer Zeile - fuer die Zelle des Ausdrucks. */ |
| 660 | readonly datenlage: string; |
| 661 | /** Gleichung und Datenlage fuer die eigene Spalte der Tabellenausgabe. */ |
| 662 | readonly gleichungSpalte: string; |
| 663 | /** |
| 664 | * Lastzuganteil am Schwerverkehr als Prozentzahl OHNE Einheit, fuer eine |
| 665 | * auswertbare Tabellenspalte - der leere Text, wo keiner erfasst ist. Leer |
| 666 | * und "0" sind verschiedene Angaben: "0" heisst gezaehlt und ohne Lastzuege, |
| 667 | * leer heisst nicht gezaehlt. |
| 668 | */ |
| 669 | readonly lastzuganteilProzent: string; |
| 670 | /** |
| 671 | * Schwerverkehrsanteil als Prozentzahl OHNE Einheit - der Anteil, mit dem |
| 672 | * GERECHNET wurde. |
| 673 | * |
| 674 | * NEU (Fassung 5.6.0): Ausdruck und Tabellenausgabe bildeten diese Zahl je |
| 675 | * selbst aus `demand.heavyVehicleShare`, waehrend `fsv` daneben aus dem auf 0 |
| 676 | * bis 1 begrenzten Anteil entsteht (rilsa/capacity.ts, |
| 677 | * wirksamerSchwerverkehrsanteil). Ein Projekt mit 150 % Schwerverkehr - ueber |
| 678 | * Datei und Oberflaeche nicht erreichbar, ueber Fremdwerkzeuge sehr wohl - |
| 679 | * druckte damit "150,0 %" neben "fSV = 1,900", also neben dem Faktor fuer |
| 680 | * 100 %. Der Pruefbericht hat denselben Befund und dieselbe Loesung; dass der |
| 681 | * eingetragene Wert begrenzt wurde, meldet die Plannotiz |
| 682 | * `schwerverkehrsanteil-ausserhalb`. |
| 683 | */ |
| 684 | readonly anteilProzent: string; |
| 685 | } |
| 686 | |
| 687 | export function schwerverkehrsangabe( |
| 688 | demand: Pick<TrafficDemand, 'heavyVehicleShare' | 'lastzugAnteil'>, |
| 689 | ): Schwerverkehrsangabe { |
| 690 | const lastzugAnteil = demand.lastzugAnteil; |
| 691 | const pSV = wirksamerSchwerverkehrsanteil(demand.heavyVehicleShare); |
| 692 | const fsv = schwerverkehrsfaktor(pSV, aufteilungAusLastzuganteil(pSV, lastzugAnteil)); |
| 693 | const anteilProzent = fmt.numShort(pSV * 100, 1); |
| 694 | const annahme = |
| 695 | `Annahme ${fmt.percent(HBS_LASTZUGANTEIL_BEI_UNBEKANNTER_AUFTEILUNG)} Lastzüge am ` + |
| 696 | 'Schwerverkehr'; |
| 697 | |
| 698 | if (istLastzuganteil(lastzugAnteil)) { |
| 699 | return { |
| 700 | fsv, |
| 701 | mitAufteilung: true, |
| 702 | gleichungKurz: 'Gl. 2-5', |
| 703 | // "des Schwerverkehrs" ausgeschrieben, obwohl die Zelle damit umbricht: |
| 704 | // Die Bezugsgroesse ist der Schwerverkehr und nicht die |
| 705 | // Gesamtverkehrsstaerke, und "Lastzüge 40 %" neben "12,0 %" liesse genau |
| 706 | // diese Verwechslung zu. |
| 707 | datenlage: `Lastzüge ${fmt.numShort(lastzugAnteil * 100, 1)} % des Schwerverkehrs`, |
| 708 | gleichungSpalte: '2-5 (Aufteilung erfasst)', |
| 709 | lastzuganteilProzent: fmt.numShort(lastzugAnteil * 100, 1), |
| 710 | anteilProzent, |
| 711 | }; |
| 712 | } |
| 713 | |
| 714 | /* |
| 715 | * Eingetragen, aber unbrauchbar (ausserhalb 0 bis 1 oder keine Zahl): Der |
| 716 | * Fachkern VERWIRFT den Wert und rechnet nach Gl. 2-6 (capacity.ts, |
| 717 | * istLastzuganteil). In der Unterlage darf daraus nicht "nicht erfasst" |
| 718 | * werden - der Bearbeiter hat etwas eingetragen, und ein Ausdruck, der das |
| 719 | * verschweigt, macht aus dem Verwerfen eine stille Ersetzung. Der |
| 720 | * Pruefbericht meldet den Fall zusaetzlich als Warnung |
| 721 | * ('lastzuganteil-ausserhalb'). |
| 722 | * |
| 723 | * Der Linter meldet die Abfrage als immer falsch: `istLastzuganteil` traegt |
| 724 | * das Praedikat `wert is number`, also verengt der Uebersetzer den |
| 725 | * Nein-Zweig auf `undefined`. Das Praedikat ist zu grosszuegig - es weist |
| 726 | * auch -0,3, 1,5 und NaN ab, und die sind Zahlen. Genau diese Eintraege |
| 727 | * sollen hier als verworfen erscheinen; bewacht in |
| 728 | * tests/export/schwerverkehrsaufteilung.test.ts. |
| 729 | */ |
| 730 | // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- das Praedikat ist zu grosszuegig; siehe darueber |
| 731 | const verworfen = lastzugAnteil !== undefined; |
| 732 | return { |
| 733 | fsv, |
| 734 | mitAufteilung: false, |
| 735 | gleichungKurz: 'Gl. 2-6', |
| 736 | datenlage: verworfen ? 'Aufteilung unbrauchbar, verworfen' : 'Aufteilung nicht erfasst', |
| 737 | gleichungSpalte: verworfen |
| 738 | ? `2-6 (Eintrag zum Lastzuganteil unbrauchbar und verworfen; ${annahme})` |
| 739 | : `2-6 (keine Aufteilung erfasst; ${annahme})`, |
| 740 | lastzuganteilProzent: '', |
| 741 | anteilProzent, |
| 742 | }; |
| 743 | } |
| 744 | |
| 745 | /** |
| 746 | * Wie viele Stroeme dieses Plans nach welcher Gleichung rechnen. |
| 747 | * |
| 748 | * WARUM DER AUSDRUCK DAS ZAEHLEN MUSS (Fassung 5.6.0): Seit beide Gleichungen |
| 749 | * im Einsatz sind, ist "wie gerechnet wurde" keine Programmeigenschaft mehr, |
| 750 | * sondern eine Eigenschaft DIESES Plans. Ein Erlaeuterungsabsatz, der beide |
| 751 | * Gleichungen nennt, ohne zu sagen, welche hier gegriffen hat, laesst den |
| 752 | * Pruefer im Unklaren; einer, der pauschal die Fehlerrichtung der fehlenden |
| 753 | * Aufteilung beschreibt, obwohl jeder Strom gezaehlt ist, behauptet einen |
| 754 | * Vorbehalt, den dieser Plan nicht hat. |
| 755 | * |
| 756 | * GEZAEHLT WIRD, WOFUER EIN FAKTOR GEDRUCKT WIRD: dieselbe Bedingung wie in |
| 757 | * der Zelle des Ausdrucks (Kfz und OePNV, Verkehrsstaerke groesser als 0). |
| 758 | * Sonst zaehlte der Absatz Stroeme mit, die in der Tabelle einen Strich tragen. |
| 759 | */ |
| 760 | export interface Aufteilungslage { |
| 761 | /** Stroeme mit erfasster Aufteilung - sie rechnen nach Gl. 2-5. */ |
| 762 | readonly mitAufteilung: number; |
| 763 | /** Stroeme ohne - sie rechnen nach Gl. 2-6. */ |
| 764 | readonly ohneAufteilung: number; |
| 765 | } |
| 766 | |
| 767 | export function aufteilungslage(project: Project): Aufteilungslage { |
| 768 | let mit = 0; |
| 769 | let ohne = 0; |
| 770 | for (const gruppe of project.signalGroups) { |
| 771 | if (!hatSaettigungsverkehrsstaerke(gruppe.mode)) continue; |
| 772 | const demand = project.demands.find((d) => d.signalGroupId === gruppe.id); |
| 773 | if (!demand || !Number.isFinite(demand.volume) || demand.volume <= 0) continue; |
| 774 | if (istLastzuganteil(demand.lastzugAnteil)) mit += 1; |
| 775 | else ohne += 1; |
| 776 | } |
| 777 | return { mitAufteilung: mit, ohneAufteilung: ohne }; |
| 778 | } |
| 779 | |
| 780 | /** |
| 781 | * Wie der Schwerverkehr in die Saettigungsverkehrsstaerke eingeht |
| 782 | * (Fassung 5.5.0, Frage E6). |
| 783 | * |
| 784 | * KORREKTUR: Hier stand "Der Schwerverkehrsanteil mindert die |
| 785 | * Saettigungsverkehrsstaerke (Pkw-Aequivalent 2,0)". Beides war falsch |
| 786 | * beschriftet - die 2,0 sind der Wert des HCM 2010, gedruckt unter der |
| 787 | * Ueberschrift HBS 2015, und ein Pruefer konnte aus dem Satz weder den |
| 788 | * Zeitbedarfswert noch die gedruckte Saettigungsverkehrsstaerke herleiten. |
| 789 | * Jetzt steht die Kette da, mit der das HBS rechnet: fSV, tB, qS. |
| 790 | * |
| 791 | * Der Zeitbedarfswert kommt aus der angesetzten Saettigungsverkehrsstaerke |
| 792 | * (tB = 3600/qS) und nicht als ausgeschriebene 1,8 s: qS ist ein Vorgabenfeld. |
| 793 | * Wer es aendert, bekaeme sonst einen Satz gedruckt, der zu seinen eigenen |
| 794 | * Zahlen nicht passt (Befund C21). |
| 795 | * |
| 796 | * WAS NICHT ABGEBILDET IST, STEHT DABEI: Die Anpassungsfaktoren f1 und f2 des |
| 797 | * HBS fehlen. Ihre Fehlerrichtung ist die unsichere - ohne sie faellt die |
| 798 | * Saettigungsverkehrsstaerke einer schmalen, engen oder steigenden Zufahrt zu |
| 799 | * gross aus. |
| 800 | * |
| 801 | * NACHGETRAGEN (Fassung 5.5.0, Befund F2): Zur fehlenden AUFTEILUNG des |
| 802 | * Schwerverkehrs stand hier nur die Tatsache ("erfasst keine Aufteilung"), |
| 803 | * nicht die Fehlerrichtung - dabei ist sie der groesste unsichere Posten der |
| 804 | * ganzen Umstellung. Gl. 2-6 rechnet jedes Schwerfahrzeug mit dem |
| 805 | * Pkw-Gleichwert 1,9; ein Lastzug traegt nach Gl. 2-5 aber 2,5. Bei 20 % |
| 806 | * Schwerverkehr aus lauter Lastzuegen rechnet dieses Programm 2000/1,180 = |
| 807 | * 1695 Kfz/h statt 2000/1,300 = 1538 Kfz/h, also 10,2 % zu gross - und der |
| 808 | * Fehler waechst mit dem Anteil bis auf 31,6 % bei reinem Lastzugverkehr. Diese |
| 809 | * Zahl stand in der Hilfe und in den Unterlagen, aber nicht in der Unterlage, |
| 810 | * die zur Behoerde geht. |
| 811 | * |
| 812 | * NACHGEFUEHRT (Schema 13): Der Satz "Dieses Programm erfasst keine Aufteilung |
| 813 | * des Schwerverkehrs und rechnet deshalb mit der Gleichung fuer unbekannte |
| 814 | * Aufteilung" ist seit Schema 13 falsch - die Aufteilung ist erfassbar, und beide |
| 815 | * Gleichungen sind im Einsatz, je Strom nach dessen Datenlage. Der Absatz sagt |
| 816 | * jetzt beides, nennt die Annahme hinter dem Pauschalwert samt Nachrechnung |
| 817 | * (BASt V 400, S. 10: "Bei einem Anteil der Lkw mit Anhaenger und Sattel-Kfz |
| 818 | * (Fahrzeugklasse LkwK) am Schwerverkehr von 20 % ergibt sich der |
| 819 | * Funktionsverlauf der Gleichung (2-6)"; 1,75 * 0,8 + 2,50 * 0,2 = 1,90) und |
| 820 | * begrenzt die Fehlerrichtung auf die Stroeme, fuer die sie noch gilt. |
| 821 | * |
| 822 | * KORREKTUR (Fassung 5.10.0): Der Absatz nannte die Kette nur bis fSV und sagte |
| 823 | * daneben zu, in der Spalte "Saettigungsverkehrsstaerke" stuenden bei reinem |
| 824 | * Pkw-Verkehr die 2.000 Fz/h je Fahrstreifen. Die Spalte fuehrt aber |
| 825 | * saturationFlow(movement, lanes, defaults) / fSV - also auch die |
| 826 | * Fahrstreifenzahl und, bei abbiegenden Stroemen, die Abminderung fA aus den |
| 827 | * Vorgaben. Ein einstreifiger Abbieger ohne Schwerverkehr steht mit 1.800 Fz/h |
| 828 | * da, und der Absatz behauptete daneben 2.000. Er schloss zugleich mit dem |
| 829 | * Anspruch, an dem er scheiterte ("ohne ihn liesse sich die |
| 830 | * Saettigungsverkehrsstaerke ... nicht nachvollziehen"). Jetzt steht die |
| 831 | * vollstaendige Kette da, mit fA aus denselben Vorgaben. |
| 832 | * |
| 833 | * EIN ZEICHEN, EINE GROESSE: Die erste Berichtigung fuehrte |
| 834 | * qS in einem Satz fuer zwei verschiedene Groessen - einmal als 3600/tB, also |
| 835 | * je Fahrstreifen und einschliesslich fSV, einmal als Wert des ganzen Stroms. |
| 836 | * Woertlich genommen stimmten beide nur fuer n = 1 und fA = 1, und wer die eine |
| 837 | * in die andere einsetzte, teilte ein zweites Mal durch fSV. Der Grundwert je |
| 838 | * Fahrstreifen heisst jetzt qS0; die Spalte fuehrt qS0 · n · fA / fSV. |
| 839 | * |
| 840 | * `lage` ist die Datenlage DIESES Plans (aufteilungslage). Wer sie nicht |
| 841 | * durchreicht, bekommt die vollstaendige Fassung mit der Fehlerrichtung: Der |
| 842 | * Vorbehalt darf nur entfallen, wenn NACHGEWIESEN ist, dass jeder Strom eine |
| 843 | * erfasste Aufteilung hat - nicht schon dann, wenn der Aufrufer nichts weiss. |
| 844 | */ |
| 845 | export function schwerverkehrSatz( |
| 846 | defaults: RilsaDefaults = RILSA_DEFAULTS, |
| 847 | lage?: Aufteilungslage, |
| 848 | ): string { |
| 849 | const qS = defaults.capacity.saturationFlow; |
| 850 | // Der Zeitbedarfswert ist der Kehrwert der Saettigungsverkehrsstaerke; ohne |
| 851 | // gueltige qS gibt es keinen, und dann bleibt die Kette ungenannt, statt eine |
| 852 | // Zahl aus einer Division durch 0 zu drucken. |
| 853 | const tB = qS > 0 ? `${fmt.numShort(3600 / qS, 2)} s` : 'dem Zeitbedarfswert des Regelwerts'; |
| 854 | const pauschal = fmt.numShort(HBS_PKW_GLEICHWERTE.schwerverkehrPauschal, 2); |
| 855 | const zuschlag = fmt.numShort(HBS_PKW_GLEICHWERTE.schwerverkehrPauschal - 1, 2); |
| 856 | const lkwUndBus = fmt.numShort(HBS_PKW_GLEICHWERTE.lkwUndBus, 2); |
| 857 | const lastzug = fmt.numShort(HBS_PKW_GLEICHWERTE.lkwMitAnhaenger, 2); |
| 858 | const lastzuganteil = HBS_LASTZUGANTEIL_BEI_UNBEKANNTER_AUFTEILUNG; |
| 859 | |
| 860 | // Die VOLLSTAENDIGE Kette der gedruckten Spalte (Fassung 5.10.0): Sie fuehrt |
| 861 | // saturationFlow(movement, lanes, defaults) / fSV, also auch die |
| 862 | // Fahrstreifenzahl und - bei 'links' und 'rechts' - die Abminderung fuer |
| 863 | // abbiegende Stroeme. Der Absatz nannte nur fSV und sagte daneben die 2.000 |
| 864 | // Fz/h je Fahrstreifen als Spalteninhalt zu; bei jedem abbiegenden Strom |
| 865 | // stehen dort 1.800 Fz/h. Der Grundwert je Fahrstreifen traegt in der Kette |
| 866 | // ein eigenes Zeichen (qS0), damit qS nicht zugleich fuer den Wert des ganzen |
| 867 | // Stroms steht. |
| 868 | const fA = defaults.capacity.turningFactor; |
| 869 | |
| 870 | return ( |
| 871 | 'Der Schwerverkehr geht nach HBS 2015 über den Anpassungsfaktor fSV in den Zeitbedarfswert ' + |
| 872 | `ein: tB = fSV · ${tB} und qS = 3600 / tB; bei reinem Pkw-Verkehr (fSV = 1) ist das der ` + |
| 873 | `Grundwert qS0 = ${fmt.vehiclesPerHour(qS)} je Fahrstreifen. Die Spalte ` + |
| 874 | '"Sättigungsverkehrsstärke" nennt den Wert des ganzen Stroms und damit die vollständige ' + |
| 875 | 'Kette qS0 · n · fA / fSV: n ist die Fahrstreifenzahl, fA die Abminderung für abbiegende ' + |
| 876 | `Ströme (fA = ${fmt.numShort(fA, 2)} für links und rechts abbiegende, fA = 1 für alle ` + |
| 877 | 'übrigen). Ein einstreifiger abbiegender Strom ohne Schwerverkehr steht deshalb mit ' + |
| 878 | `${fmt.vehiclesPerHour(qS * fA)} in der Spalte und nicht mit ${fmt.vehiclesPerHour(qS)}. ` + |
| 879 | 'Welche der ' + |
| 880 | 'beiden Gleichungen des HBS gilt, entscheidet die Datenlage DES EINZELNEN STROMS – so sieht ' + |
| 881 | 'es das Regelwerk vor („in Abhängigkeit von der Datenverfügbarkeit zur Aufteilung des ' + |
| 882 | 'Schwerverkehrs"): Ist an seiner Verkehrsstärke der Anteil der Lastzüge (Lkw mit Anhänger ' + |
| 883 | `und Sattel-Kfz) am Schwerverkehr erfasst, gilt fSV = (qLV + ${lkwUndBus} · qLkw+Bus + ` + |
| 884 | `${lastzug} · qLkwK) / qKfz (Gl. 2-5); fehlt er, gilt fSV = (qLV + ${pauschal} · qSV) / qKfz, ` + |
| 885 | `also fSV = 1 + ${zuschlag} · pSV (Gl. 2-6). Die Spalte "Schwerverkehr" nennt für jeden Strom ` + |
| 886 | 'den Anteil pSV, die Aufteilung – soweit erfasst – und den daraus gebildeten Faktor mit ' + |
| 887 | 'seiner Gleichung; ohne ihn ließe sich die Sättigungsverkehrsstärke aus dieser Kette nicht ' + |
| 888 | `nachvollziehen. ${lageSatz(lage)}DIESELBE RECHNUNG, ZWEI DATENLAGEN: ` + |
| 889 | `Hinter dem Pauschalwert ${fmt.num(HBS_PKW_GLEICHWERTE.schwerverkehrPauschal, 2)} steht die ` + |
| 890 | `Annahme, dass ${fmt.percent(lastzuganteil)} des Schwerverkehrs Lastzüge sind ` + |
| 891 | `(${fmt.num(HBS_PKW_GLEICHWERTE.lkwUndBus, 2)} · ${fmt.num(1 - lastzuganteil, 2)} + ` + |
| 892 | `${fmt.num(HBS_PKW_GLEICHWERTE.lkwMitAnhaenger, 2)} · ${fmt.num(lastzuganteil, 2)} = ` + |
| 893 | `${fmt.num(HBS_PKW_GLEICHWERTE.schwerverkehrPauschal, 2)}, ohne Rundung); Gl. 2-6 ist damit ` + |
| 894 | `Gl. 2-5 mit ${fmt.percent(lastzuganteil)} Lastzügen. Wer diesen Anteil einträgt, ändert an ` + |
| 895 | 'seinen Zahlen nichts: Die Erfassung der Aufteilung ist kein anderes Verfahren, sondern ' + |
| 896 | `dieselbe Rechnung mit einer Angabe mehr. ${fehlerrichtungSatz(qS, lage)}` + |
| 897 | 'Die geometrischen Anpassungsfaktoren des HBS für Fahrstreifenbreite, Kurvenradius und ' + |
| 898 | 'Längsneigung sind seit 5.30.0 abgebildet. Sie greifen nur, wo die Zufahrt vermessen und ' + |
| 899 | 'eingetragen ist; ohne Eintrag gilt die Standardbedingung ' + |
| 900 | `(Längsneigung höchstens ${fmt.numShort(HBS_STANDARDBEDINGUNGEN.laengsneigungBis)} %, ` + |
| 901 | `Kurvenradius mindestens ${fmt.meters(HBS_STANDARDBEDINGUNGEN.kurvenradiusAb, 0)}, ` + |
| 902 | `Fahrstreifenbreite mindestens ${fmt.meters(HBS_STANDARDBEDINGUNGEN.fahrstreifenbreiteAb, 0)}), ` + |
| 903 | 'und die Sättigungsverkehrsstärke fällt für eine davon abweichende Zufahrt zu groß aus. ' + |
| 904 | 'Die Spalte „Geometrie" der Leistungstabelle sagt je Strom, ob eine Vermessung vorliegt.' |
| 905 | ); |
| 906 | } |
| 907 | |
| 908 | /** |
| 909 | * Was in DIESEM Plan gerechnet wurde - mit abschliessendem Leerzeichen, damit |
| 910 | * der Absatz ohne Datenlage keine doppelte Luecke bekommt. |
| 911 | * |
| 912 | * Die Zahlen stehen da, nicht die Namen: Welcher Strom nach welcher Gleichung |
| 913 | * rechnet, sagt die Tabelle Zeile fuer Zeile; hier geht es um die Frage, ob der |
| 914 | * Pruefer im Folgenden ueberhaupt nach Stroemen ohne Aufteilung suchen muss. |
| 915 | */ |
| 916 | function lageSatz(lage: Aufteilungslage | undefined): string { |
| 917 | if (lage === undefined) return ''; |
| 918 | const { mitAufteilung, ohneAufteilung } = lage; |
| 919 | if (mitAufteilung + ohneAufteilung === 0) return ''; |
| 920 | if (ohneAufteilung === 0) { |
| 921 | return ( |
| 922 | `In diesem Plan ist die Aufteilung für ${stroeme(mitAufteilung)} erfasst; gerechnet wurde ` + |
| 923 | 'durchgehend nach Gl. 2-5. ' |
| 924 | ); |
| 925 | } |
| 926 | if (mitAufteilung === 0) { |
| 927 | return ( |
| 928 | `In diesem Plan ist für keinen der ${stroeme(ohneAufteilung)} eine Aufteilung erfasst; ` + |
| 929 | 'gerechnet wurde durchgehend nach Gl. 2-6. ' |
| 930 | ); |
| 931 | } |
| 932 | return ( |
| 933 | `In diesem Plan rechnen ${stroeme(mitAufteilung)} nach Gl. 2-5 und ${stroeme(ohneAufteilung)} ` + |
| 934 | 'nach Gl. 2-6. ' |
| 935 | ); |
| 936 | } |
| 937 | |
| 938 | /** "1 Strom" oder "3 Ströme" - eine Unterlage schreibt keine "1 Ströme". */ |
| 939 | function stroeme(anzahl: number): string { |
| 940 | return anzahl === 1 ? '1 Strom' : `${fmt.numShort(anzahl, 0)} Ströme`; |
| 941 | } |
| 942 | |
| 943 | /** |
| 944 | * Die Fehlerrichtung der fehlenden Aufteilung - an einem gerechneten Beispiel |
| 945 | * und mit ihrer Obergrenze (Fassung 5.5.0, Befund F2), begrenzt auf die Stroeme, fuer die sie |
| 946 | * gilt. |
| 947 | * |
| 948 | * DIE ZAHLEN KOMMEN AUS DER RECHENFUNKTION und nicht aus dem Satz: Die |
| 949 | * Pkw-Gleichwerte stehen in HBS_PKW_GLEICHWERTE, die Spanne bildet |
| 950 | * `lastzuganteilSpanne` - dieselbe Funktion, mit der der Pruefbericht die |
| 951 | * Spanne je Strom beziffert. Ein Ausdruck, der eine Prozentzahl behauptet, die |
| 952 | * zur eigenen Rechnung nicht passt, ist genau der Befund, um den es hier geht. |
| 953 | * |
| 954 | * DAS BEISPIEL BLEIBT BEI 20 % SCHWERVERKEHR und wird nicht auf den groessten |
| 955 | * Anteil dieses Plans umgestellt: Der Absatz erklaert die Gleichung, nicht den |
| 956 | * einzelnen Strom. Fuer den einzelnen Strom rechnet der Pruefbericht die |
| 957 | * Spanne aus seinem eigenen Anteil aus - darauf verweist der letzte Satz. |
| 958 | */ |
| 959 | function fehlerrichtungSatz(qS: number, lage: Aufteilungslage | undefined): string { |
| 960 | if (lage !== undefined && lage.ohneAufteilung === 0) { |
| 961 | return ( |
| 962 | 'Der Vorbehalt, der sonst an Gl. 2-6 hängt – der Pauschalwert unterschätzt einen ' + |
| 963 | 'lastzuglastigen Strom –, geht damit in keine Zahl dieses Plans ein. ' |
| 964 | ); |
| 965 | } |
| 966 | const beispiel = 0.2; |
| 967 | const spanne = lastzuganteilSpanne(beispiel); |
| 968 | const pauschal = fmt.numShort(HBS_PKW_GLEICHWERTE.schwerverkehrPauschal, 2); |
| 969 | const lastzug = fmt.numShort(HBS_PKW_GLEICHWERTE.lkwMitAnhaenger, 2); |
| 970 | return ( |
| 971 | 'FEHLERRICHTUNG, WO DIE AUFTEILUNG FEHLT: Gl. 2-6 rechnet jedes Schwerfahrzeug mit dem ' + |
| 972 | `Pkw-Gleichwert ${pauschal}; ein Lastzug trägt nach Gl. 2-5 aber ${lastzug}. Besteht der ` + |
| 973 | 'Schwerverkehr eines solchen Stroms überwiegend aus Lastzügen, fällt seine ' + |
| 974 | `Sättigungsverkehrsstärke ZU GROSS aus – bei ${fmt.percent(beispiel)} Schwerverkehr aus ` + |
| 975 | `lauter Lastzügen um ${fmt.percent(spanne.hoechstensZuGross, 1)} ` + |
| 976 | `(${fmt.vehiclesPerHour(qS / spanne.pauschal)} statt ` + |
| 977 | `${fmt.vehiclesPerHour(qS / spanne.nurLastzuege)}), mit steigendem Anteil bis zu ` + |
| 978 | `${fmt.percent(lastzuganteilSpanne(1).hoechstensZuGross, 1)}; enthält er keine Lastzüge, ` + |
| 979 | `rechnet das Programm ${fmt.percent(spanne.hoechstensZuKlein, 1)} zu klein, also auf der ` + |
| 980 | 'sicheren Seite. Kapazität, Auslastungsgrad und Qualitätsstufe eines lastzuglastigen Stroms ' + |
| 981 | 'ohne erfasste Aufteilung fallen entsprechend günstiger aus, als sie sind; wo der ' + |
| 982 | // KEINE MELDESCHWELLE IM SATZ (SCHWERVERKEHR_AUFTEILUNG_MELDESCHWELLE in |
| 983 | // validation/rules.ts): Ob der Bericht einen Strom anspricht, entscheidet |
| 984 | // der Fachkern. "Der Prüfbericht beziffert es je Strom" waere fuer einen |
| 985 | // Strom mit 5 % Schwerverkehr schlicht falsch - dort schweigt er. |
| 986 | 'Schwerverkehrsanteil ins Gewicht fällt, beziffert der Prüfbericht beide Richtungen für den ' + |
| 987 | 'Anteil des jeweiligen Stroms. Für Ströme MIT erfasster Aufteilung entfällt dieser Posten. ' |
| 988 | ); |
| 989 | } |
| 990 | |
| 991 | /** |
| 992 | * Wirksamer Wert und - falls er davon abweicht - der Regelwert dahinter. |
| 993 | * |
| 994 | * Ein Fundstellenverzeichnis, das nur den Regelwert nennt, behauptet vor der |
| 995 | * Behoerde eine Uebereinstimmung, die der Plan nicht hat; eines, das nur den |
| 996 | * wirksamen Wert nennt, verschweigt die Abweichung. Es stehen beide da. |
| 997 | */ |
| 998 | function wirksamMitRegelwert(wirksam: string, regelwert: string): string { |
| 999 | return wirksam === regelwert ? wirksam : `${wirksam} – abweichend vom Regelwert ${regelwert}`; |
| 1000 | } |
| 1001 | |
| 1002 | /** |
| 1003 | * VwV-StVO zu Par. 37: Einsatzgrenze und Signalfolge - fuer jede Anlagenart. |
| 1004 | * |
| 1005 | * `defaults` sind die Kennwerte, mit denen gerechnet wird (Befund C21). Die |
| 1006 | * Ueberschrift behauptete frueher "keine einstellbaren Vorgaben"; das stimmt |
| 1007 | * fuer die Einsatzgrenze und die Rot-Gelb-Obergrenze, nicht aber fuer die |
| 1008 | * Rot-Gelb-Zeit und die Gelbzeitstaffel - beide sind Vorgabenfelder. |
| 1009 | */ |
| 1010 | function vwvStvoEintrag(defaults: RilsaDefaults): QuelleOhneFeld { |
| 1011 | return { |
| 1012 | schluessel: 'vwv-stvo', |
| 1013 | quelle: QUELLEN['vwv-stvo'], |
| 1014 | bezeichnung: |
| 1015 | 'Einsatzgrenze der Lichtsignalanlage und Signalfolge (Einsatzgrenze und Rot-Gelb-Obergrenze ' + |
| 1016 | 'sind harte Schranken; Rot-Gelb-Zeit und Gelbzeitstaffel sind einstellbar – hier steht der ' + |
| 1017 | 'wirksame Wert)', |
| 1018 | kennwerte: [ |
| 1019 | { |
| 1020 | label: 'Zulässige Höchstgeschwindigkeit für den Betrieb einer LSA', |
| 1021 | wert: `bis ${fmt.numShort(VZUL_OBERGRENZE_LSA)} km/h (Rn. 10)`, |
| 1022 | }, |
| 1023 | { |
| 1024 | label: 'Rot-Gelb-Zeit', |
| 1025 | wert: |
| 1026 | `${wirksamMitRegelwert(fmt.seconds(defaults.redYellow), fmt.seconds(RILSA_DEFAULTS.redYellow))}, ` + |
| 1027 | `höchstens ${fmt.seconds(SAFETY_FLOORS.maxRedYellow)} (Rn. 17)`, |
| 1028 | }, |
| 1029 | { |
| 1030 | label: 'Gelbzeit nach zulässiger Höchstgeschwindigkeit', |
| 1031 | wert: `${wirksamMitRegelwert(gelbzeitstaffelSatz(defaults), gelbzeitstaffelSatz())} (Rn. 17)`, |
| 1032 | }, |
| 1033 | ], |
| 1034 | }; |
| 1035 | } |
| 1036 | |
| 1037 | /** |
| 1038 | * RSA 21: Anhaltswerte der einstreifigen Verkehrsfuehrung - nur dort. Die |
| 1039 | * Zeile zu 400/600 m steht dabei, obwohl sie KEIN Wert der RSA ist: Der |
| 1040 | * Pruefbericht nennt diese Laengen im selben Atemzug wie die RSA-Kriterien, |
| 1041 | * und der Nachweis muss sagen, welche der Zahlen aus dem Regelwerk stammt und |
| 1042 | * welche aus der Behoerdenpraxis (Befund C5). |
| 1043 | */ |
| 1044 | function rsaEintrag(): QuelleOhneFeld { |
| 1045 | return { |
| 1046 | schluessel: 'rsa-arbeitsstellen', |
| 1047 | quelle: QUELLEN['rsa-arbeitsstellen'], |
| 1048 | bezeichnung: |
| 1049 | 'Anhaltswerte für die Hinweise des Prüfberichts bei einstreifiger Verkehrsführung (keine ' + |
| 1050 | 'Rechengröße; die Umlaufzeit- und Wartezeitschranken der Anlagenart stehen im Abschnitt ' + |
| 1051 | '„Schranken der Anlagenart")', |
| 1052 | kennwerte: [ |
| 1053 | { |
| 1054 | label: 'Kurze Engstelle, für die eine Regelung ohne Lichtsignalanlage genügen kann', |
| 1055 | wert: |
| 1056 | `bis ${fmt.meters(RSA_EINSTREIFIG.kurzeEngstelleBis, 0)} (Regelung nach § 6 StVO bzw. mit den ` + |
| 1057 | 'Zeichen 208/308; Teil B)', |
| 1058 | }, |
| 1059 | { |
| 1060 | label: 'Richtwert der Verkehrsstärke für die Anordnung (beide Richtungen zusammen)', |
| 1061 | wert: `über ${fmt.numShort(RSA_EINSTREIFIG.verkehrsstaerkeRichtwert)} Kfz/h (Teil B)`, |
| 1062 | }, |
| 1063 | { |
| 1064 | label: 'Zulässige Höchstgeschwindigkeit an Arbeitsstellen längerer Dauer', |
| 1065 | wert: `in der Regel ${fmt.numShort(RSA_EINSTREIFIG.vZulRegel)} km/h (Teil C 2.3.2)`, |
| 1066 | }, |
| 1067 | { |
| 1068 | label: |
| 1069 | `Engstellenlänge ${fmt.meters(ENGSTELLE_PRAXIS.hinweisAb, 0)} / ` + |
| 1070 | `${fmt.meters(ENGSTELLE_PRAXIS.warnungAb, 0)} (Hinweis / Warnung)`, |
| 1071 | wert: |
| 1072 | 'Behördenpraxis (z. B. Hessen Mobil), KEIN Wert der RSA - Baustellenampeln werden ' + |
| 1073 | `üblicherweise bis ${fmt.meters(ENGSTELLE_PRAXIS.hinweisAb, 0)} aufgestellt, ausnahmsweise ` + |
| 1074 | `und mit Zustimmung bis ${fmt.meters(ENGSTELLE_PRAXIS.warnungAb, 0)}`, |
| 1075 | }, |
| 1076 | ], |
| 1077 | }; |
| 1078 | } |
| 1079 | |
| 1080 | /** |
| 1081 | * Was das Fundstellenverzeichnis je Kennwert nennt: den Regelwert - und, wo |
| 1082 | * dieser Plan davon abweicht, den angesetzten Wert. |
| 1083 | */ |
| 1084 | export interface Fundstellenkennwert { |
| 1085 | /** Der Wert, gegen den die Abweichungsliste misst. */ |
| 1086 | readonly regelwert: number; |
| 1087 | /** Angesetzter Wert, wo diese Planung abweicht; sonst null. */ |
| 1088 | readonly angesetzt: number | null; |
| 1089 | } |
| 1090 | |
| 1091 | /** |
| 1092 | * Regelwert und angesetzter Wert je Kennwert - beides aus dem Fachkern, keiner |
| 1093 | * davon hier noch einmal entschieden. |
| 1094 | * |
| 1095 | * Der Regelwert ist das Feld `regelwert` der Vorgabenzeile (settings.ts, |
| 1096 | * `regelwertVon`) - die eine Stelle, aus der auch die Abweichungsliste ihren |
| 1097 | * Bezug nimmt und aus der die Vorgabenansicht liest. Er haengt allein an der |
| 1098 | * Anlagenart und nie am Eintrag des Anwenders: Eine Spalte "Regelwert", die |
| 1099 | * zwischen 40 und 60 s wechselt, je nachdem ob jemand etwas eingetragen hat, |
| 1100 | * waere ein Widerspruch in einer Zeile. WAS ANGESETZT WIRD, sagt dagegen die |
| 1101 | * Abweichungsliste (`resolveRilsaSettings`), also dieselbe Funktion, aus der |
| 1102 | * die gedruckte Aufstellung "Abweichungen von den Regelwerten" entsteht. |
| 1103 | * |
| 1104 | * KORREKTUR (Fassung 5.12.0): Das Verzeichnis bildete beides selbst - aus |
| 1105 | * `settingFields(...).standard`, also der Zahl des Regelwerkssatzes, und einem |
| 1106 | * Wertvergleich daneben. Bei den beiden Regelbereichsfeldern der Umlaufzeit ist |
| 1107 | * der Bezug aber der Wert, der OHNE Eintrag fuer die ANLAGENART gilt; an einer |
| 1108 | * Fussgaengerschutzanlage nannte dieselbe Unterlage damit zwei Regelwerte fuer |
| 1109 | * denselben Kennwert (40 gegen 60 s). |
| 1110 | * |
| 1111 | * EINE QUELLE, KEIN UMWEG MEHR (Fassung 5.13.0): Bis dahin |
| 1112 | * holte diese Funktion den Regelwert ueber eine zweite Abweichungsliste - eine |
| 1113 | * Vorlage, in der jeder Kennwert auf `standard` stand, durch |
| 1114 | * `resolveRilsaSettings` gefuehrt, und der Bezug aus deren `standardValue` |
| 1115 | * gelesen. Seit der Fassung 5.12.0 fuehrt die Vorgabenzeile |
| 1116 | * denselben Wert unmittelbar; gemessen ueber alle 45 Kennwerte und alle drei |
| 1117 | * Anlagenarten liefern beide Wege dieselbe Zahl. Dass die Vorgabenzeile und die |
| 1118 | * Abweichungsliste zusammenbleiben, haelt |
| 1119 | * tests/domain/regelwertBezugJeFeld.test.ts fest - fuer jedes Feld und jede |
| 1120 | * Anlagenart, nicht nur fuer die beiden Regelbereichsfelder. |
| 1121 | */ |
| 1122 | export function fundstellenkennwerte( |
| 1123 | overrides: RilsaOverrides | undefined, |
| 1124 | anlagenart: Anlagenart = 'knotenpunkt', |
| 1125 | ): ReadonlyMap<keyof RilsaOverrides, Fundstellenkennwert> { |
| 1126 | const abweichung = new Map( |
| 1127 | resolveRilsaSettings(overrides, RILSA_DEFAULTS, anlagenart).deviations.map( |
| 1128 | (d): readonly [keyof RilsaOverrides, SettingDeviation] => [d.key, d], |
| 1129 | ), |
| 1130 | ); |
| 1131 | |
| 1132 | return new Map( |
| 1133 | settingFields(RILSA_DEFAULTS, anlagenart).map( |
| 1134 | (f): readonly [keyof RilsaOverrides, Fundstellenkennwert] => [ |
| 1135 | f.key, |
| 1136 | { |
| 1137 | regelwert: f.regelwert, |
| 1138 | angesetzt: abweichung.get(f.key)?.appliedValue ?? null, |
| 1139 | }, |
| 1140 | ], |
| 1141 | ), |
| 1142 | ); |
| 1143 | } |
| 1144 | |
| 1145 | /** |
| 1146 | * Zusatz zur Kennwertbeschriftung im Fundstellenverzeichnis, wenn ein Kennwert |
| 1147 | * in dieser Planung in keine Zahl eingeht - in Klammern und mit dem Grund: |
| 1148 | * " (ohne Wirkung: Bewertungsverfahren HCM)" bzw. " (ohne Wirkung in dieser |
| 1149 | * Fassung)". Sonst der leere Text. |
| 1150 | * |
| 1151 | * WELCHE Kennwerte das sind, entscheidet der Fachkern |
| 1152 | * (settings.ts, `wirkungslosigkeit`) - hier steht nur noch die Klammer. |
| 1153 | * Vor Fassung 5.4.0 (Befund C20) entschied diese Funktion es |
| 1154 | * selbst und kannte dabei nur den Instationaritaetsfaktor; die |
| 1155 | * Koordinierungs-Kennwerte standen im Nachweis wie angewandte Werte, obwohl |
| 1156 | * coordination.ts von keiner Rechnung aufgerufen wird. |
| 1157 | * |
| 1158 | * Der `kontext` traegt die projektabhaengigen Faelle (Fassung 5.5.0, Fragen E1 und E2): |
| 1159 | * die beiden festen Ueberfahrzeiten, die nur beim Ansatz 'fest' wirken, und die |
| 1160 | * Raeumgeschwindigkeit bei engem Innenradius, die nur wirkt, wenn wenigstens |
| 1161 | * eine Beziehung das Merkmal traegt. Jedes Feld ist optional wie im Fachkern - |
| 1162 | * wer es nicht durchreicht, bekommt "der Wert wirkt" und damit eine |
| 1163 | * Begruendungspflicht zu viel statt einer Wirkungsbehauptung zu wenig. Jeder |
| 1164 | * Aufrufer, der das Projekt kennt, reicht den Kontext durch. |
| 1165 | */ |
| 1166 | export function wirkungsvermerk( |
| 1167 | quellenSchluessel: QuellenSchluessel, |
| 1168 | verfahren: Bewertungsverfahren, |
| 1169 | kontext: WirkungsKontext = {}, |
| 1170 | ): string { |
| 1171 | const ohneWirkung = wirkungslosigkeit(quellenSchluessel, verfahren, kontext); |
| 1172 | return ohneWirkung === null ? '' : ` (${ohneWirkung.vermerk})`; |
| 1173 | } |