lsa-planer
LSA-Planer Professional – Planungssoftware für Lichtsignalanlagen nach RiLSA 2015 und § 45 StVO. EUPL-1.2.
/ src domain rilsa signalTimes.ts
| 1 | import type { KilometersPerHour, Meters, MetersPerSecond, Seconds } from '../units'; |
| 2 | import { isFiniteNumber } from '../units'; |
| 3 | import { OEPNV_UEBERGANGSZEIT_STAFFEL, RILSA_DEFAULTS, type RilsaDefaults } from './constants'; |
| 4 | import type { SignalGroupTimes, TrafficMode } from './types'; |
| 5 | |
| 6 | /** |
| 7 | * Gelbzeit einer Signalgruppe. |
| 8 | * |
| 9 | * KORREKTUR gegenueber dem Altbestand: Dort lieferte die Funktion fuer |
| 10 | * Fussgaenger die Gelbzeit des Radverkehrs (3 s), weil der Fussgaenger-Fall |
| 11 | * durch den Sammelzweig am Ende fiel. Fussgaengersignale haben kein Gelb. |
| 12 | */ |
| 13 | export function yellowTime( |
| 14 | mode: TrafficMode, |
| 15 | vZul: KilometersPerHour, |
| 16 | defaults: RilsaDefaults = RILSA_DEFAULTS, |
| 17 | ): Seconds { |
| 18 | switch (mode) { |
| 19 | case 'fuss': |
| 20 | return 0; |
| 21 | case 'rad': |
| 22 | return defaults.yellowRad; |
| 23 | case 'oepnv': |
| 24 | /* |
| 25 | * KORREKTUR (Fassung 5.27.0): Hier stand 0 s mit dem Vermerk |
| 26 | * "Sondersignale (Balkensignale) kennen kein Gelb". Sie kennen kein |
| 27 | * GELB, aber sehr wohl eine Uebergangszeit: das Uebergangssignal nach |
| 28 | * Par. 51 BOStrab, dessen Dauer die RiLSA 2015 in Abschnitt 2.4 nach der |
| 29 | * betrieblich zugelassenen Hoechstgeschwindigkeit staffelt (4 s bis |
| 30 | * 30 km/h bis 8 s bei 70 km/h). Begruendung und die beiden nicht |
| 31 | * abgebildeten Ausnahmen stehen bei OEPNV_UEBERGANGSZEIT_STAFFEL. |
| 32 | */ |
| 33 | return oepnvUebergangszeit(vZul); |
| 34 | case 'kfz': { |
| 35 | if (!isFiniteNumber(vZul) || vZul <= 0) { |
| 36 | // Ohne belastbare Geschwindigkeitsangabe wird die laengste Gelbzeit |
| 37 | // angesetzt. Das ist die sichere Seite; die Validierung meldet die |
| 38 | // fehlende Angabe zusaetzlich als Fehler. |
| 39 | return lastYellowStep(defaults); |
| 40 | } |
| 41 | for (const step of defaults.yellowBySpeed) { |
| 42 | if (vZul <= step.upToKmh) return step.yellow; |
| 43 | } |
| 44 | return lastYellowStep(defaults); |
| 45 | } |
| 46 | } |
| 47 | } |
| 48 | |
| 49 | /** |
| 50 | * Dauer des Uebergangssignals eines gesondert signalisierten |
| 51 | * OePNV-Fahrzeugs nach der Staffel der RiLSA 2015 (Abschnitt 2.4). |
| 52 | * |
| 53 | * Ohne Vmax und oberhalb des Staffelendes von 70 km/h gilt die oberste Stufe - |
| 54 | * die laengere Uebergangszeit ist die sichere Seite, und oberhalb 70 km/h |
| 55 | * sollen Lichtsignalanlagen ohnehin nicht betrieben werden. |
| 56 | */ |
| 57 | function oepnvUebergangszeit(vZul: KilometersPerHour): Seconds { |
| 58 | const oberste = OEPNV_UEBERGANGSZEIT_STAFFEL.at(-1)?.uebergang ?? 8; |
| 59 | if (!isFiniteNumber(vZul) || vZul <= 0) return oberste; |
| 60 | for (const stufe of OEPNV_UEBERGANGSZEIT_STAFFEL) { |
| 61 | if (vZul <= stufe.upToKmh) return stufe.uebergang; |
| 62 | } |
| 63 | return oberste; |
| 64 | } |
| 65 | |
| 66 | function lastYellowStep(defaults: RilsaDefaults): Seconds { |
| 67 | const last = defaults.yellowBySpeed[defaults.yellowBySpeed.length - 1]; |
| 68 | return last ? last.yellow : 5; |
| 69 | } |
| 70 | |
| 71 | /** |
| 72 | * Rot-Gelb-Zeit einer Signalgruppe. |
| 73 | * Fussgaenger- und OePNV-Sondersignale haben kein Rot-Gelb. |
| 74 | */ |
| 75 | export function redYellowTime( |
| 76 | mode: TrafficMode, |
| 77 | defaults: RilsaDefaults = RILSA_DEFAULTS, |
| 78 | ): Seconds { |
| 79 | return mode === 'fuss' || mode === 'oepnv' ? 0 : defaults.redYellow; |
| 80 | } |
| 81 | |
| 82 | /** |
| 83 | * Mindestfreigabezeit einer Signalgruppe - der REGELWERT je Verkehrsart. |
| 84 | * |
| 85 | * Fuer Fussgaengergruppen ist das nur die Untergrenze: Daneben gilt die |
| 86 | * Furtregel (`furtFreigabebedarf`), und massgebend ist der groessere Wert. |
| 87 | * Beides zusammen rechnet `mindestfreigabezeitDerGruppe` in |
| 88 | * src/domain/plan/signalPlan.ts, weil dafuer die Konfliktbeziehungen des |
| 89 | * Projekts gebraucht werden. |
| 90 | */ |
| 91 | export function minGreenTime(mode: TrafficMode, defaults: RilsaDefaults = RILSA_DEFAULTS): Seconds { |
| 92 | return defaults.minGreen[mode]; |
| 93 | } |
| 94 | |
| 95 | /** |
| 96 | * Freigabezeitbedarf einer Fussgaengerfurt nach der Furtregel der RiLSA 2015. |
| 97 | * |
| 98 | * Ein Fussgaenger, der zu Beginn der Freigabezeit die Furt betritt, muss |
| 99 | * waehrend der Freigabe die HALBE Furt zuruecklegen koennen; mit einer |
| 100 | * Zusatzeinrichtung fuer Blinde und Sehbehinderte (DIN 32981) die GANZE Furt. |
| 101 | * Zeitbedarf = Furtweg / vr, mit der Raeumgeschwindigkeit der Gruppe |
| 102 | * (Regelwert 1,2 m/s; 1,0 m/s bei erhoehtem Zeitbedarf). |
| 103 | * |
| 104 | * NEU (Fassung 5.4.0, Befund C1): Bis dahin galt fuer jede |
| 105 | * Fussgaengergruppe allein die feste Mindestfreigabezeit von 5 s. Eine 20 m |
| 106 | * lange Furt erhielt damit 5 s Freigabe, obwohl schon die halbe Furt 8,3 s |
| 107 | * verlangt - der Fussgaenger stand nach Freigabeende noch 3,3 s vor der Mitte. |
| 108 | * |
| 109 | * Alle Groessen stehen im Ergebnis, damit die Meldung den Rechenweg nennen kann |
| 110 | * und ein Pruefer ihn nachrechnet. `zeitbedarf` ist ungerundet; |
| 111 | * `zeitbedarfGerundet` ist der Wert, mit dem der Plan rechnet (ganze Sekunden, |
| 112 | * aufgerundet) - HIER gerundet und nicht beim Aufrufer, weil die Rundungsregel |
| 113 | * sonst an jeder Ausgabestelle noch einmal stand (signalPlan.ts zweimal, pdf.ts |
| 114 | * einmal) und beim naechsten Aufrufer auseinanderliefe. |
| 115 | */ |
| 116 | export interface FurtFreigabebedarf { |
| 117 | /** Furtlaenge in Metern. */ |
| 118 | readonly furtlaenge: Meters; |
| 119 | /** Ganze Furt (Zusatzeinrichtung fuer Blinde und Sehbehinderte) oder halbe Furt. */ |
| 120 | readonly ganzeFurt: boolean; |
| 121 | /** Angesetzter Furtweg: Furtlaenge bzw. Furtlaenge / 2. */ |
| 122 | readonly furtweg: Meters; |
| 123 | /** Angesetzte Raeumgeschwindigkeit vr in m/s. */ |
| 124 | readonly vr: MetersPerSecond; |
| 125 | /** Zeitbedarf furtweg / vr in Sekunden, ungerundet. */ |
| 126 | readonly zeitbedarf: Seconds; |
| 127 | /** |
| 128 | * Zeitbedarf auf ganze Sekunden AUFGERUNDET - der Wert, der in den Plan |
| 129 | * eingeht. Aufgerundet, nie abgerundet: Signalzeiten werden in ganzen |
| 130 | * Sekunden geschaltet, und 5,83 s Bedarf sind mit 5 s nicht gedeckt. |
| 131 | */ |
| 132 | readonly zeitbedarfGerundet: Seconds; |
| 133 | } |
| 134 | |
| 135 | /** |
| 136 | * Toleranz gegen Gleitkommarauschen beim Aufrunden: 8,4 m / 1,2 m/s ist |
| 137 | * rechnerisch 7,000000000000001 s und darf nicht 8 s ergeben. Eine echte |
| 138 | * Ueberschreitung um weniger als eine Mikrosekunde gibt es bei Wegen in |
| 139 | * Metern (zwei Nachkommastellen) und Geschwindigkeiten mit einer |
| 140 | * Nachkommastelle nicht. |
| 141 | */ |
| 142 | const RUNDUNGSTOLERANZ: Seconds = 1e-6; |
| 143 | |
| 144 | export function furtFreigabebedarf( |
| 145 | furtlaenge: Meters, |
| 146 | vr: MetersPerSecond, |
| 147 | ganzeFurt: boolean, |
| 148 | ): FurtFreigabebedarf { |
| 149 | const furtweg = ganzeFurt ? furtlaenge : furtlaenge / 2; |
| 150 | // vr <= 0 kann aus den Vorgaben nicht kommen (Schranken in settings.ts); |
| 151 | // hier trotzdem abgesichert, damit nie "Infinity s" entsteht. |
| 152 | const zeitbedarf = vr > 0 ? furtweg / vr : 0; |
| 153 | const zeitbedarfGerundet = Math.max(0, Math.ceil(zeitbedarf - RUNDUNGSTOLERANZ)); |
| 154 | return { furtlaenge, ganzeFurt, furtweg, vr, zeitbedarf, zeitbedarfGerundet }; |
| 155 | } |
| 156 | |
| 157 | /** Alle Signalzeiten einer Signalgruppe in einem Zug. */ |
| 158 | export function signalGroupTimes( |
| 159 | mode: TrafficMode, |
| 160 | vZul: KilometersPerHour, |
| 161 | defaults: RilsaDefaults = RILSA_DEFAULTS, |
| 162 | ): SignalGroupTimes { |
| 163 | return { |
| 164 | minGreen: minGreenTime(mode, defaults), |
| 165 | yellow: yellowTime(mode, vZul, defaults), |
| 166 | redYellow: redYellowTime(mode, defaults), |
| 167 | }; |
| 168 | } |
| 169 | |
| 170 | /** |
| 171 | * Dauer eines vollstaendigen Signalwechsels von Rot nach Rot: |
| 172 | * Rot-Gelb + Freigabezeit + Gelb. |
| 173 | */ |
| 174 | export function signalGroupCycleSpan(times: SignalGroupTimes, green: Seconds): Seconds { |
| 175 | return times.redYellow + green + times.yellow; |
| 176 | } |