import type { KilometersPerHour, MetersPerSecond, Meters, Ratio, Seconds } from '../units'; import { ceilSeconds, isFiniteNumber, roundTo } from '../units'; import { RILSA_ENGSTELLE } from './constants'; import type { CalculationNote } from './types'; /** * Das eigene Rechenverfahren der Engstellensignalisierung * (RiLSA 2015, Abschnitt 5.2). * * WARUM DAS EIN ZWEITES VERFAHREN IST UND KEINE VARIANTE DES ERSTEN: Die RiLSA * sagt in Abschnitt 5.2.1, die Ausfuehrungen gelten "primaer fuer die * transportablen, das heisst voruebergehend eingesetzten Lichtsignalanlagen * (Baustellenampeln) an Arbeitsstellen" - also genau fuer die Anlagenart * `einstreifig` dieses Programms. Und sie rechnet dort anders als am * Knotenpunkt, in fuenf Punkten: * * 1. Die Ueberfahrzeit betraegt 4 s, unabhaengig von allem uebrigen. Am * Knotenpunkt sind es 3 s geradeaus bzw. 2 s abbiegend (Faelle 1 und 2) * oder, im Ansatz dieses Programms, die Gelbzeit. * 2. Die Raeumgeschwindigkeit haengt an der zulaessigen * Hoechstgeschwindigkeit und wird in km/h angesetzt, nicht in m/s. * 3. Der Raeumweg ist der Abstand der Haltlinien - OHNE Fahrzeuglaenge. Am * Knotenpunkt kommen 6 m hinzu. * 4. Eine Einfahrzeit wird NICHT abgezogen ("Die Zwischenzeit tz wird ohne * Beruecksichtigung einer Einfahrzeit ermittelt"). * 5. Auch die Uebergangszeit GELB betraegt 4 s statt der 3/4/5 s der * Gelbzeitstaffel. * * BIS ZUR FASSUNG 5.26.0 rechnete dieses Programm die einstreifige * Verkehrsfuehrung nach den Knotenpunktregeln. Das war nicht durchweg falsch - * fuer einen nachgerechneten angeordneten Plan (Raeumweg 40 m, zulaessig * 50 km/h) kommen beide Wege auf dieselben 8 s -, aber es war eine andere * Rechnung mit anderen Groessen, und bei anderer Geometrie laufen sie * auseinander. Bei 40 km/h zulaessiger Hoechstgeschwindigkeit tut sie das in * die VERKUERZENDE Richtung: 200 m Raeumweg ergeben nach Abschnitt 5.2 * 4 + 200/30 * 3,6 = 28 s, nach den Knotenpunktregeln 3 + 206/10 = 24 s. * * DIESES MODUL RECHNET NUR; es entscheidet nicht, wann es gilt. Das tut * `resolveIntergreens` in plan/signalPlan.ts anhand der Anlagenart. */ /** Woher die angesetzte Raeumgeschwindigkeit einer Engstelle kommt. */ export type EngstellenRaeumgrund = /** Staffel nach der zulaessigen Hoechstgeschwindigkeit. */ | 'vzul' /** Schlechte Fahrbahn oder regelmaessig landwirtschaftliche Fahrzeuge. */ | 'fahrbahn' /** Der Radverkehr ist massgebend. */ | 'radverkehr' /** Unterhalb der Staffel: die kleinste Stufe. */ | 'unter-staffel' /** Auf die zulaessige Hoechstgeschwindigkeit gedeckelt. */ | 'vmax-deckel'; export interface EngstellenRaeumgeschwindigkeit { /** Angesetzte Raeumgeschwindigkeit Vr in km/h - die Einheit des Abschnitts. */ readonly vrKmh: KilometersPerHour; readonly grund: EngstellenRaeumgrund; } export interface EngstellenAnnahmen { /** * Schlechte Fahrbahn oder regelmaessig auftretende landwirtschaftliche * Fahrzeuge: dann 30 km/h, unabhaengig von der zulaessigen * Hoechstgeschwindigkeit. */ readonly schlechteFahrbahn?: boolean; /** * Der Radverkehr ist massgebend - die Engstelle ist nicht so breit und * uebersichtlich, dass sich Kraftfahrzeuge und Radfahrer unbedenklich * begegnen koennen. Dann 18 km/h. */ readonly radverkehrMassgebend?: boolean; } /** * Raeumgeschwindigkeit einer Engstelle nach Abschnitt 5.2.2. * * Die Staffel nennt drei Stufen (50/40/30 km/h bei zulaessig 60/50/40 km/h). * Unterhalb von 40 km/h endet sie; angesetzt wird dann die kleinste Stufe - * eine kleinere Raeumgeschwindigkeit ergaebe eine laengere Zwischenzeit, und * die RiLSA nennt fuer diesen Fall keinen Wert. Oberhalb von 60 km/h ebenso * die oberste: Dort duerfte ohnehin keine Baustellenampel stehen (die RSA * beschraenkt Arbeitsstellen laengerer Dauer in der Regel auf 50 km/h). * * DIE BEIDEN SONDERFAELLE GEHEN VOR, und der langsamere von beiden gewinnt: * Beide setzen die Geschwindigkeit HERAB, und wo beide zutreffen, gilt die * kleinere - eine Engstelle wird nicht dadurch schneller, dass zwei Gruende * fuer Langsamkeit zusammentreffen. */ export function engstellenRaeumgeschwindigkeit( vZul: KilometersPerHour, annahmen: EngstellenAnnahmen = {}, ): EngstellenRaeumgeschwindigkeit { const sonderfaelle: EngstellenRaeumgeschwindigkeit[] = []; if (annahmen.radverkehrMassgebend === true) { sonderfaelle.push({ vrKmh: RILSA_ENGSTELLE.vrRadverkehr, grund: 'radverkehr' }); } if (annahmen.schlechteFahrbahn === true) { sonderfaelle.push({ vrKmh: RILSA_ENGSTELLE.vrSchlechteFahrbahn, grund: 'fahrbahn' }); } if (sonderfaelle.length > 0) { return sonderfaelle.reduce((a, b) => (b.vrKmh < a.vrKmh ? b : a)); } const staffel = RILSA_ENGSTELLE.raeumgeschwindigkeit; const kleinste = staffel[staffel.length - 1]; if (!isFiniteNumber(vZul) || vZul <= 0) { // Ohne Angabe die kleinste Stufe: Sie ergibt die laengste Zwischenzeit. return { vrKmh: kleinste?.vrKmh ?? 30, grund: 'unter-staffel' }; } let gewaehlt: EngstellenRaeumgeschwindigkeit = { vrKmh: kleinste?.vrKmh ?? 30, grund: 'unter-staffel', }; for (const stufe of staffel) { if (vZul >= stufe.vZulAb) { gewaehlt = { vrKmh: stufe.vrKmh, grund: 'vzul' }; break; } } /* * NICHT SCHNELLER RAEUMEN, ALS GEFAHREN WERDEN DARF - derselbe Deckel wie * am Knotenpunkt (Fassung 5.22.0, `vmaxDeckel` in intergreen.ts). * * Er wird hier gebraucht, weil die Staffel bei 40 km/h endet: Unterhalb * davon gilt ihre kleinste Stufe, 30 km/h - und die liegt bei einer * Arbeitsstelle mit Tempo 20 ueber der zulaessigen Hoechstgeschwindigkeit. * Ein Fahrzeug raeumte dann rechnerisch schneller, als es fahren darf. Der * Deckel ist ein Ansatz DIESES Programms; die RiLSA nennt fuer diesen Fall * keinen Wert. Er wirkt nur nach unten und verlaengert die Zwischenzeit. */ if (vZul < gewaehlt.vrKmh) return { vrKmh: vZul, grund: 'vmax-deckel' }; return gewaehlt; } export interface EngstellenZwischenzeit { /** Zwischenzeit in ganzen Sekunden, aufgerundet. */ readonly zwischenzeit: Seconds; /** Ungerundetes Ergebnis tue + sr/Vr * 3,6. */ readonly roh: Seconds; /** Angesetzte Ueberfahrzeit - nach Abschnitt 5.2.2 stets 4 s. */ readonly ueberfahrzeit: Seconds; /** Raeumweg sr: Abstand der Haltlinien, OHNE Fahrzeuglaenge. */ readonly raeumweg: Meters; /** Angesetzte Raeumgeschwindigkeit mit ihrer Begruendung. */ readonly raeumgeschwindigkeit: EngstellenRaeumgeschwindigkeit; /** Raeumzeit sr/Vr * 3,6 in Sekunden. */ readonly raeumzeit: Seconds; readonly notes: readonly CalculationNote[]; } /** * Zwischenzeit einer Engstelle nach Abschnitt 5.2.2: * * tz = tue + sr / Vr * 3,6 mit tue = 4 s und Vr in km/h * * OHNE EINFAHRZEIT - das steht dort ausdruecklich, und es ist der Grund, warum * diese Funktion keinen einfahrenden Strom kennt. Wer hier eine Einfahrzeit * abzoege, verkuerzte die Zwischenzeit gegenueber dem Regelwerk. */ export function engstellenZwischenzeit( raeumweg: Meters, vZul: KilometersPerHour, annahmen: EngstellenAnnahmen = {}, ): EngstellenZwischenzeit { const notes: CalculationNote[] = []; const sr = isFiniteNumber(raeumweg) && raeumweg > 0 ? raeumweg : 0; if (sr === 0) { notes.push({ severity: 'warnung', code: 'engstelle-raeumweg-null', message: 'Der Räumweg der Engstelle beträgt 0 m. Nach RiLSA 2015, Abschnitt 5.2.2 ist der Abstand ' + 'zwischen den Haltlinien anzusetzen - bei nicht markierten Haltlinien der Abstand der ' + 'Signalgeber.', }); } const raeumgeschwindigkeit = engstellenRaeumgeschwindigkeit(vZul, annahmen); const raeumzeit = (sr / raeumgeschwindigkeit.vrKmh) * 3.6; const roh = RILSA_ENGSTELLE.ueberfahrzeit + raeumzeit; return { zwischenzeit: ceilSeconds(roh), roh: roundTo(roh, 3), ueberfahrzeit: RILSA_ENGSTELLE.ueberfahrzeit, raeumweg: sr, raeumgeschwindigkeit, raeumzeit: roundTo(raeumzeit, 3), notes, }; } /** Die Raeumgeschwindigkeit einer Engstelle in SI-Einheiten. */ export function engstellenRaeumgeschwindigkeitMs( vZul: KilometersPerHour, annahmen: EngstellenAnnahmen = {}, ): MetersPerSecond { return engstellenRaeumgeschwindigkeit(vZul, annahmen).vrKmh / 3.6; } /** * Wartezeitminimale Umlaufzeit einer Engstelle nach Abschnitt 5.2.3: * * tU = (1,3 * TZ + 4) / (1 - (q1/qS1 + q2/qS2)) mit Summe < 1 * * Dieselbe Bauart wie Webster, aber mit anderen Beiwerten UND einer anderen * Bezugsgroesse: TZ ist die Summe der beiden ZWISCHENzeiten, nicht die * Verlustzeit je Umlauf. Die Anfahrverluste, die dieses Programm am * Knotenpunkt in L einrechnet, kommen hier nicht vor. * * Die Umlaufzeit soll nicht groesser als 300 s sein. Dass dieses Programm * dieselbe Schranke bereits fuehrte, ist ein Zufall der Herleitung: Sie war * bis 5.26.0 aus den uebrigen Schranken gerechnet und ausdruecklich als * "kein Wert eines Regelwerks" ausgewiesen. Jetzt ist sie einer. */ export function engstellenUmlaufzeit(zwischenzeitSumme: Seconds, auslastungssumme: Ratio): Seconds { const nenner = 1 - auslastungssumme; if (!(nenner > 0)) return Number.POSITIVE_INFINITY; return ( (RILSA_ENGSTELLE.umlaufzeitFaktor * zwischenzeitSumme + RILSA_ENGSTELLE.umlaufzeitZuschlag) / nenner ); } /** * Freigabezeit einer Fahrtrichtung nach Abschnitt 5.2.3: * * tFi = (qi/qSi) / (q1/qS1 + q2/qS2) * (tU - TZ) * * Das ist dieselbe Aufteilung wie am Knotenpunkt (Abschnitt 2.7.1) - die * verbleibende Zeit im Verhaeltnis der Auslastungsgrade. Sie steht hier * gleichwohl eigens, weil die RiLSA sie eigens nennt und weil ein Pruefer sie * an dieser Fundstelle sucht. */ export function engstellenFreigabezeit( auslastung: Ratio, auslastungssumme: Ratio, umlaufzeit: Seconds, zwischenzeitSumme: Seconds, ): Seconds { if (!(auslastungssumme > 0)) return 0; return (auslastung / auslastungssumme) * (umlaufzeit - zwischenzeitSumme); }