Im Zuge einer Vorlesung im Fach Software Engineering an der HS Trier im Februar 2015 wurden in einigen Nebensätzen sozusagen prominente Fälle gescheiterter Projekte mit softwarelastigem Hintergrund besprochen.
Aufgrund technischem Interesse an Luft- und Raumfahrt ist mir der erwähnte Vorfall der Ariane 5 besonders nachhaltig in Erinnerung geblieben, der sich Mitte der 90er Jahre ereignet hat.
Was war geschehen? Das neuentwickelte Trägersystem „Ariane 5“ Startnummer V88 hob am 04. Juni 1996 um 12:34 Uhr GMT mit 1 Stunde Verzögerung zum ersten Mal vom Weltraumbahnhof Kuorou in französisch Guayana ab. Nach ca. 40 Sekunden explodierte das System. Außer einem Sachschaden in mehrstelliger Millionenhöhe wurden die vier an Bord transportierten Sateliten zerstört – also Know-How und Entwicklungsarbeit vernichtet – aber es gab keine menschlichen Opfer zu beklagen. Der Vorfall überwarf den Plan um über 3 Jahre in die Zukunft. Ein Testflug im Oktober 1997 fand nur mit Satelitenattrapen und dem von Studenten aus über 10 Ländern entwickelten YES-Sateliten statt. Der erste kommerzielle Flug fand 3 Jahre später statt. Die vier ursprünglichen Sateliten wurden vier Jahre (!) später von russischen Systemen in die Erdumlaufbahn befördert.
Hintergrund zum Trägersystem
Das Vorgänger-Trägersystem Ariane 4 wurde von 1982 bis 1988 entwickelt, d.h. die Entwicklungszeit nahm 6 Jahre (!) in Anspruch. Das System Ariane 5 wurde in 1988 zur Entwicklung freigegeben und im Jahre 1996 fertig gestellt – ein Jahr nach Plan, da der Erstflug bereits im Oktober 1995 hätte stattfinden sollen. Nur um es zu verdeutlichen: die Entwicklungszeit dieses Systems dauerte 8 Jahre! Betrachtet man diesen langen Zeitraum (in dieser Branche eine Ewigkeit) aus Sicht der Projektphasen Anforderung, Analyse, Entwicklung, Bau und Test möglicher Komponenten UND den Umstand, dass damals vermutlich das Verhältnis 50/50 oder 60/40 von Entwicklung zu Test einherging UND die Schwierigkeit dass ein einziges für dieses Großprojekt gegründete französische Unternehmen eine Vielzahl von Zulieferern organisieren musste sowie den kompletten technischen und wirtschaftlichen Part zu verantworten hatte, so werden 8 Jahre schlicht kurz. Dazu kommt, dass ein solch komplexes System nicht vollständig getestet werden kann. Es gab den Erstflug und der führte zu einem Fehlstart. Fertig.
Operanden-Fehler verursachte fehlerhafte Flugdaten
Die Flugdaten des Trägheitsnavigationssystems (auch „inertiales Navigationssystem“ genannt, inertial von lat. iner=träge) lieferten dem Hauptcomputer Daten, die an das Steuersystem weitergegeben und verarbeitet wurden. Genau genommen arbeitete alles gemäß der Softwareprogrammierung.
Hier eine vereinfachte Darstellung des Systems.
Abb: einfaches UML über Abhängigkeiten OCS, SRI und Sensoren.
Der Hauptcomputer (OCS, Onboard Computer System) hat also zwei Navigationssysteme zur Verfügung. Das aktive SRI2 (Inertial Reference System, Trägeheits-Navigationssystem) hat die Aufgabe, über Gyroskope und Geschwindigkeitsmesser – also Sensoren, die an der Trägerrakete angebracht wurden – Navigationsdaten an den Hauptcomputer weiterzugeben, der diese Daten zur Berechnung und Anpassung der Flugbahn benötigt. Das im Hot-Standby befindliche Backupsystem SRI1 war als redundantes Hardwaresystem auch mit indentischer Software ausgestattet.
E_BH verursachte den Fehler
Der Code für das Trägheits-Navigationssystem SRI wurde der Ariane 4 entnommen. Eine nach der Bergung der Überreste gegründete Expertenkommission fand nach 4 Wochen den eigentlichen Fehler.
Abb: Quellcode Programmiersprache ADA
Vereinfacht kann die Zeile so abgebildet werden:
PM_DERIVE(T_ALG.E_BH) =UC_16S_EN_16NS(E_ENTIER_16S ( (1 / C_M_LSB_BH) * G_M_INFO_DERIVE(T_ALG.E_BH))
Mich würde brennend interessieren, was UC_16S_EN_16NS genau macht. Versucht die Funktion eine Konvertierung von 16S signed in EN 16 NS nonsigned oder kann diese beide Arten Signed und Unsigned verarbeiten? Dann vermute ich hinter der C_M_LSB_BH eine Konstante sowbei die Variable LSB ein Kürzel für „Large Super Booster“ – die Flüssiggas-Tanks mit Brenner – sein kann. Sicher ist, dass E_BH in die Berechnung ständig einfließt, siehe rechten Term, hinten. Es heisst auch, dass der E_BH aus 64-bittigen floating-Werten berechnet wird. Das kann dann nur die C_M_LSB_BH oder der Multiplikant G_M_INFO_DERIVE (kann G_M etwas mit Gyro-Blabla, also der Wert der Sensorenphalanx sein?) sein. Trotz mehrere Suchen im Internet fand ich hierzu nichts bedeutendes.
Der Wert für die horizontale Ausrichtung (BH, horizontal bias) war aufgrund der technischen Eigenschaften der Ariane 5 (u.a. höhere Schubkraft, höhere Horizontal- und Vertikalgeschwindigkeit) ab einem Zeitpunkt X höher als die Werte der Ariane 4.
Der fehlerhafte Wert in E_BH sorgte also für einen Absturz der Software im SRI2, lieferte vorher aber ein Fehlerwertmuster an den Hauptcomputer. Das in hotstandby befindliche SRI1 war wenige Millisekunden voher bereits down – aufgrund des gleichen Fehlers. Btw: hotstandby heißt in diesem Fall, dass eigentlich zwei Systeme ständig arbeiten so dass im Notfall der Hauptcomputer vom Backup-SRI mit einer Zeitverzögerung im Millisekundenbereich Daten erhalten kann.
Das vom SRI2 gelieferte Fehlwertmuster wurde vom OCS Hauptcomputer angenommen und ungeprüft verarbeitet. OCS führte wie befohlen aufgrund der Werte Richtungsänderungen durch wobei die Korrekturen die strukturelle Integrität der Rakete überlastete. Das Raumfahrzeug brach auseinander. Eigentlich wurde es voher von Sicherheitssystemen an Board zur Explosion gebracht bzw. wäre von der Bodenstation ausgehend gesprengt worden.
Abb: Serie von Sek. 38 bis 44 nach Liftoff
An der obigen Bilderserie ist gut zu erkennen, dass es vor der Explosion einen Richtungswechsel gegeben hat, der eine Detonation zur Folge hatte.
Wie könnte man einen solchen Fehler verhindern?
In heutiger Zeit wendet man beispielsweise für binäre Programmmodule – Software, für die also der Quellcode nicht zur Verfügung steht – sogenannte Blackbox-Tests an.
Bei Black-Box-Tests stellt man sich zu Beginn anhand sog. Äquivalenzklassen einen Satz mit Testwerten zusammen. Verlangt eine Eingabe (könnte auch der Parameter einer Funktion bzw. über eine Sensorenphalanx geschehen) einen Zahlenwert, der nicht kleiner als 0 aber auch nicht größer als 100 ist, so wäre diese Variable zu definieren mit var < 0 < 100. Dann definiert man einen Gut- und einen Schlechtwert. Ein Schlechtwert wäre hier -1 und ein Gutwert kann man als 51 definieren. Die Funktion oder die Eingabe wird nun mit diesen Werten getestet. Gibt es eine Vielzahl Eingabeparameter, so muss eigentlich eine jede Kombination getestet werden. Bei einem Eingabewert wäre das 2 (1 Gutwert und ein Schlechtwert). Bei zwei Eingabewerten wären es schon 2^2 und bei 3 zu prüfenden Werten wären es 2^3 = 8 Werte. Wieder unter Annahme, es handle sich um 10 zu prüfende Zahlewerten mit jeweils einem Gut- und einem Schlechtwert, dann wären 2^10 = 1024 Möglichkeiten zu prüfen. Im technischen Umfeld Maschinen- oder Anlagenbau sowie in der Elektrotechnik gibt es oft eine Vielzahl von Parametern. Dann sollte von der Testperson das Ergebnis interpretiert werden können. Alles nicht so einfach.
Vorhandener Quellcode – also sog. Whiteboxes – kann dagegen mit Whitebox-Testverfahren getestet werden.
Dabei kann der Quellcode generellen Analysen unterzogen werden: z.B. mit der sog. „zyklomatischen Zahl“. Der Code wird zunächst als sog. Programmablaufgraph abgebildet. Alle Kanten, d.h. alle if-else- und Schleifen-Anweisungen werden gezählt, genau so die Kanten von Anweisung zu Anweisung. Eine if-else ist eine Kante. Eine Schleife ist ebenso eine Kante. Ein Knoten kann eine Anweisung sein. Dann wird die Zahl berechnet mit Z = e – n + 2. Wobei: e = edges und n = nodes.
Beispiel anhand eines 5-Zeilers:
Abb: Code und der Programmablaufgraph
Obiges Beispiel hat 10 Kanten und 9 Knoten (Start und Stop mit gezählt). Die zyklomatische Zahl lautet hier: 10 – 9 + 2 = 3. Man liest: die Zahl sollte den Wert 10 nicht überschreiten.
Ist der Programmablaufgraph erstellt, kann anhand daran der Quellcode nachvollzogen werden und zwar mit mehreren verschiedenen Verfahren:
Anweisungsüberdeckende Tests, d.h. der Test sollte alle Konten abdecken. Im obigen Fall wäre das Testmenge i= 2 oder 3.
Zweigüberdeckende Tests, d.h. alle Kanten/Zweige sollten mind. 1 mal durchlaufen werden. Oben wären das bei Testmenge i = 3 und 5;
Dann gibt es noch den pfadüberdeckenden Test, bei denen alle Prüfungen mind. 1x erfolgreich und 1x nicht erfolgreich sein sollen. Schleifen sollen zudem komplett durchlaufen werden. Die Testsätze vermehren sich entsprechend.
Abschließend ist wichtig zu wissen: Quellcode soll gut dokumentiert werden. Die Funktionsparameter sollen in Funktionsköpfen dokumentiert werden ebenso was die Funktion genau macht mit einem entsprechenden Rückgabewert.
Die Aussage „ich bin gleich fertig – ich muss nur noch Testen“ darf es so nicht geben. Ein Verhältnis Entwicklung/Test von 50/50 oder 60/40 ist nicht außergewöhnlich.
In der Flug- und Raumfahrttechnik gibt es bspw. Levels, die anhand Konsequenzen festgelegt werden: katastrophal (Level A), gefährlich/schwerwiegend (Level B), erheblich (Level C) und geringfügig (Level D) und unbedeutend (Level E). Nach Standard DO-178B muss eine 100%-iger Überdeckungsnachweis anhand obiger drei Testverfahren gebracht werden.
Ob das auch eine Übernahme von funktionierendem Code aus alten Systemen einschließt? „Normal scho…“