Aerospace Tutorial: Von der Anforderung zum Nachweis

Dieses Tutorial führt Schritt für Schritt durch den vollständigen Entwicklungszyklus eines sicherheitskritischen Lyx-Programms — von der Projektstruktur über das Schreiben von Safety-konformem Code bis hin zur Erzeugung aller Zertifizierungsnachweise nach DO-178C (Guide).

Als durchgehendes Beispiel dient ein Altitude Monitor: eine Komponente, die Druckrohdaten in Höhenmeter umrechnet, Grenzwerte überwacht und einen Warnung-Status ausgibt. Einfach genug, um den Fokus auf den Prozess zu legen — komplex genug, um alle relevanten Safety-Features zu zeigen.

Zielgruppe: Entwickler, die erstmals ein DO-178C-konformes Programm mit Lyx erstellen oder den gesamten Toolchain-Workflow kennenlernen wollen.

1. Projektstruktur anlegen

Eine klare Verzeichnisstruktur ist die Grundlage für nachvollziehbare, auditierbare Builds. DO-178C verlangt, dass Quellcode, Tests und Nachweise klar voneinander getrennt sind.

mkdir -p altitude_monitor/{src,tests,build,evidence}
cd altitude_monitor

Verzeichnis Inhalt
src/ Produktiver Quellcode (.lyx-Dateien)
tests/ Testfälle und Testprogramme
build/ Compiler-Ausgaben (Binaries, .lyu-Units)
evidence/ Zertifizierungsnachweise (Reports, Logs, Graphs)

Struktur nach dem Tutorial:

altitude_monitor/
├── src/
│   ├── types.lyx          # Gemeinsame Typdefinitionen
│   └── altitude.lyx       # Hauptkomponente
├── tests/
│   └── altitude_test.lyx  # Testprogramm
├── build/
│   ├── altitude.elf
│   └── altitude_test.elf
└── evidence/
    ├── call_graph.dot
    ├── coverage.html
    ├── static_analysis.log
    ├── stack_report.log
    └── provenance.log


2. Typdefinitionen schreiben

Alle gemeinsamen Typen kommen in eine eigene Datei. Range-Typen (Typaliase & Typumwandlung) sind das erste Sicherheitsnetz — sie machen ungültige Zustände auf Typebene unmöglich.

Datei: src/types.lyx

unit AltitudeMonitor.Types;

// Physikalische Wertebereiche — Compile-Zeit- und Laufzeit-geprüft
type Altitude    = int64 range -1000..15000;   // Meter über MSL
type Pressure    = int64 range 10000..115000;   // Pascal (10–1150 hPa)
type Temperature = int64 range -90..60;         // Grad Celsius
type Confidence  = int64 range 0..100;          // Prozent

// Warnstatus — geschlossene Menge, kein ungültiger Wert möglich
type WarningLevel = enum {
    NOMINAL,
    CAUTION,    // Annäherung an Grenzwert
    WARNING,    // Grenzwert überschritten
    CRITICAL    // Zweiter Grenzwert überschritten — sofortiger Handlungsbedarf
};

// Sensor-Rohdaten vom ADC
type SensorFrame = struct {
    pressure_raw:    int64;
    temperature_raw: int64;
    valid:           bool;
    sequence_id:     int64;
};

// Ausgabe der Altitude-Monitor-Komponente
type AltitudeResult = struct {
    altitude:   Altitude;
    warning:    WarningLevel;
    confidence: Confidence;
    valid:      bool;
};


3. Hauptkomponente schreiben

Die Hauptkomponente enthält die eigentliche Berechnungslogik. Jede Funktion, die im Regelzyklus aufgerufen wird, bekommt die entsprechenden Safety-Pragmas.

Datei: src/altitude.lyx

unit AltitudeMonitor;

import AltitudeMonitor.Types;
import std.math;

// ── Grenzwerte ────────────────────────────────────────────────────────────────

con CAUTION_ALTITUDE:  Altitude := 12000;   // Meter — Vorsichtsgrenze
con WARNING_ALTITUDE:  Altitude := 13500;   // Meter — Warngrenze
con CRITICAL_ALTITUDE: Altitude := 14500;   // Meter — Kritische Grenze

con SEA_LEVEL_PRESSURE: f64 := 101325.0;    // Pascal
con SCALE_HEIGHT:        f64 := 8434.0;     // Meter (barometrische Formel)

con MIN_CONFIDENCE: Confidence := 70;       // Unter diesem Wert: Ergebnis ungültig

// ── Interne Hilfsfunktionen ───────────────────────────────────────────────────

// Berechnet Höhe aus Luftdruck nach barometrischer Höhenformel
// Kein @flight_crit — wird nur aus validierten Funktionen aufgerufen
@stack_limit(128)
fn PressureToAltitude(pressure_pa: f64): f64 {
    con EXPONENT: f64 := 0.190263;
    var ratio: f64 := pressure_pa / SEA_LEVEL_PRESSURE;
    return SCALE_HEIGHT * (1.0 - PowF64(ratio, EXPONENT));
}

// Berechnet Konfidenzwert aus Sensorkonsistenz (vereinfacht)
@stack_limit(128)
fn ComputeConfidence(frame: SensorFrame): Confidence {
    if (!frame.valid) { return 0; }
    // Plausibilitätsprüfung Druck vs. Temperatur (vereinfacht)
    var p_ok: bool := (frame.pressure_raw >= 10000) && (frame.pressure_raw <= 115000);
    var t_ok: bool := (frame.temperature_raw >= -90) && (frame.temperature_raw <= 60);
    if (!p_ok || !t_ok) { return 40; }
    return 95;
}

// Bestimmt Warnstufe aus berechneter Höhe
@stack_limit(128)
fn ClassifyAltitude(alt: Altitude): WarningLevel {
    if (alt >= CRITICAL_ALTITUDE) { return WarningLevel.CRITICAL; }
    if (alt >= WARNING_ALTITUDE)  { return WarningLevel.WARNING;  }
    if (alt >= CAUTION_ALTITUDE)  { return WarningLevel.CAUTION;  }
    return WarningLevel.NOMINAL;
}

// ── Hauptfunktion ─────────────────────────────────────────────────────────────

@dal(B)
@flight_crit
@stack_limit(512)
@wcet(800)
pub fn ProcessSensorFrame(frame: SensorFrame): AltitudeResult {

    // Ungültige oder nicht plausible Frames sicher ablehnen
    if (!frame.valid) {
        var _r: AltitudeResult;
        _r.altitude := 0;
        _r.warning := WarningLevel.NOMINAL;
        _r.confidence := 0;
        _r.valid := false;
        return _r;
    }

    // TMR-Schutz für den berechneten Höhenwert
    @redundant var altitude_raw: f64 :=
        PressureToAltitude(frame.pressure_raw as f64);

    // Clamping in den gültigen Range-Typ — Range-Typ übernimmt weitere Prüfung
    var alt_clamped: int64 := altitude_raw as int64;
    if (alt_clamped < -1000) { alt_clamped := -1000; }
    if (alt_clamped > 15000) { alt_clamped := 15000; }

    @redundant var altitude: Altitude := alt_clamped;

    var conf:    Confidence    := ComputeConfidence(frame);
    var warning: WarningLevel  := ClassifyAltitude(altitude);

    // Ergebnis als ungültig markieren, wenn Konfidenz zu niedrig
    var result_valid: bool := (conf >= MIN_CONFIDENCE);

    var _r: AltitudeResult;
    _r.altitude := altitude;
    _r.warning := warning;
    _r.confidence := conf;
    _r.valid := result_valid;
    return _r;
}


4. Tests schreiben

Tests in Lyx sind gewöhnliche Programme, die die zu testende Unit importieren. Für MC/DC (DO-178C) müssen alle atomaren Bedingungen unabhängig voneinander den Gesamtausdruck beeinflussen — die Testfälle müssen diese Kombinationen explizit abdecken.

Datei: tests/altitude_test.lyx

<WRAP alert>Der folgende Block benutzt std.test. Diese Unit ist noch nicht ausgeliefert — import std.test; scheitert mit Modul nicht gefunden. Die Testfaelle geben die vorgesehene Form wieder und sind nicht compilerverifiziert.</WRAP>

unit AltitudeMonitor.Tests;

import AltitudeMonitor;
import AltitudeMonitor.Types;
import std.test;

// ── Hilfsfunktion ─────────────────────────────────────────────────────────────

fn MakeFrame(pressure: int64, temp: int64, valid: bool): SensorFrame {
    var _r: SensorFrame;
    _r.pressure_raw := pressure;
    _r.temperature_raw := temp;
    _r.valid := valid;
    _r.sequence_id := 1;
    return _r;
}

// ── Testfälle für ProcessSensorFrame ─────────────────────────────────────────

// TC-01: Ungültiger Frame → result.valid = false, warning = NOMINAL
fn TC01_InvalidFrame(): void {
    var frame := MakeFrame(101325, 15, false);
    var result: AltitudeResult := ProcessSensorFrame(frame);
    test.Assert(!result.valid,                          "TC-01: result.valid soll false sein");
    test.Assert(result.warning == WarningLevel.NOMINAL, "TC-01: warning soll NOMINAL sein");
    test.Assert(result.confidence == 0,                 "TC-01: confidence soll 0 sein");
}

// TC-02: Normalbetrieb, Meeresspiegel — NOMINAL
fn TC02_NominalSealevel(): void {
    var frame := MakeFrame(101325, 15, true);
    var result: AltitudeResult := ProcessSensorFrame(frame);
    test.Assert(result.valid,                           "TC-02: result.valid soll true sein");
    test.Assert(result.warning == WarningLevel.NOMINAL, "TC-02: warning soll NOMINAL sein");
    test.Assert(result.confidence >= 70,                "TC-02: confidence soll >= 70 sein");
}

// TC-03: Reiseflug ~10.000 m — NOMINAL (unterhalb CAUTION-Grenze)
fn TC03_CruiseAltitude(): void {
    var frame := MakeFrame(26436, -45, true);  // ~10.000 m
    var result: AltitudeResult := ProcessSensorFrame(frame);
    test.Assert(result.valid,                           "TC-03: result soll valid sein");
    test.Assert(result.warning == WarningLevel.NOMINAL, "TC-03: 10.000m soll NOMINAL sein");
}

// TC-04: CAUTION-Bereich (~12.500 m)
fn TC04_CautionAltitude(): void {
    var frame := MakeFrame(19000, -55, true);  // ~12.500 m
    var result: AltitudeResult := ProcessSensorFrame(frame);
    test.Assert(result.valid,                           "TC-04: result soll valid sein");
    test.Assert(result.warning == WarningLevel.CAUTION, "TC-04: soll CAUTION sein");
}

// TC-05: WARNING-Bereich (~13.800 m)
fn TC05_WarningAltitude(): void {
    var frame := MakeFrame(16000, -60, true);  // ~13.800 m
    var result: AltitudeResult := ProcessSensorFrame(frame);
    test.Assert(result.valid,                          "TC-05: result soll valid sein");
    test.Assert(result.warning == WarningLevel.WARNING, "TC-05: soll WARNING sein");
}

// TC-06: CRITICAL-Bereich (~14.700 m)
fn TC06_CriticalAltitude(): void {
    var frame := MakeFrame(14000, -62, true);  // ~14.700 m
    var result: AltitudeResult := ProcessSensorFrame(frame);
    test.Assert(result.valid,                           "TC-06: result soll valid sein");
    test.Assert(result.warning == WarningLevel.CRITICAL, "TC-06: soll CRITICAL sein");
}

// TC-07: Druck außerhalb Plausibilitätsbereich → confidence < MIN → result.valid = false
fn TC07_ImplausiblePressure(): void {
    var frame := MakeFrame(5000, 15, true);   // Druck zu niedrig (< 10000 Pa)
    var result: AltitudeResult := ProcessSensorFrame(frame);
    test.Assert(!result.valid, "TC-07: implausible pressure soll result.valid = false ergeben");
}

// TC-08: Temperatur außerhalb Plausibilitätsbereich
fn TC08_ImplausibleTemperature(): void {
    var frame := MakeFrame(101325, 80, true);  // Temperatur zu hoch (> 60°C)
    var result: AltitudeResult := ProcessSensorFrame(frame);
    test.Assert(!result.valid, "TC-08: implausible temperature soll result.valid = false ergeben");
}

// TC-09: Grenzwert genau an CAUTION-Grenze (Boundary Value)
fn TC09_BoundaryCAUTION(): void {
    // Druck der ~12.000 m entspricht (Boundary Value Analysis)
    var frame := MakeFrame(19330, -56, true);
    var result: AltitudeResult := ProcessSensorFrame(frame);
    test.Assert(result.valid, "TC-09: Boundary-Frame soll valid sein");
    // Warning ist CAUTION oder NOMINAL je nach exaktem Rundungsverhalten
}

// TC-10: MC/DC — frame.valid=false dominiert, Pressure/Temp irrelevant
fn TC10_MCDC_InvalidOverrides(): void {
    var frame := MakeFrame(5000, 80, false);  // Beide Werte außerhalb + invalid
    var result: AltitudeResult := ProcessSensorFrame(frame);
    test.Assert(!result.valid,   "TC-10: MCDC: invalid-Flag dominiert");
    test.Assert(result.confidence == 0, "TC-10: MCDC: confidence soll 0 sein");
}

// ── Einstiegspunkt ────────────────────────────────────────────────────────────

fn main(): int64 {
    test.Begin("AltitudeMonitor — Testlauf");

    TC01_InvalidFrame();
    TC02_NominalSealevel();
    TC03_CruiseAltitude();
    TC04_CautionAltitude();
    TC05_WarningAltitude();
    TC06_CriticalAltitude();
    TC07_ImplausiblePressure();
    TC08_ImplausibleTemperature();
    TC09_BoundaryCAUTION();
    TC10_MCDC_InvalidOverrides();

    test.End();
    return 0;
}


5. Safety-Build durchführen

Der erste Build dient der Überprüfung: Sind alle Safety-Regeln eingehalten? Compiliert der Code ohne Warnungen?

# Typen kompilieren (vorkompilierte Unit erzeugen)
lyxc src/types.lyx \
    --compile-unit \
    --lint \
    -o build/types.lyu

# Hauptkomponente kompilieren — vollständige Safety-Prüfung
lyxc src/altitude.lyx \
    -I build/ \
    --lint \
    --static-analysis \
    --target=arm64 \
    -o build/altitude.elf

Erwartete Ausgabe bei korrektem Code:

[lint]   AltitudeMonitor — OK (0 warnings, 0 errors)
[stack]  ProcessSensorFrame: 312 bytes / 512 limit — OK
[stack]  PressureToAltitude: 96 bytes / 128 limit — OK
[stack]  ComputeConfidence: 80 bytes / 128 limit — OK
[stack]  ClassifyAltitude: 64 bytes / 128 limit — OK
[build]  build/altitude.elf — OK

Häufige Lint-Fehler und ihre Bedeutung:

Fehlermeldung Bedeutung Lösung
heap allocation in @flight_crit function new innerhalb einer @flight_crit-Funktion new in die Initialisierungsphase verschieben
unbounded loop in @flight_crit context Schleife ohne limit(N) limit(N) hinzufügen
missing @stack_limit on @flight_crit function Kein Stack-Budget definiert @stack_limit(N) ergänzen
range violation: value 16000 exceeds Altitude range Konstante verletzt Range-Typ Konstante oder Typ korrigieren
@wcet annotation missing for DAL-B function DAL-B verlangt WCET-Budget @wcet(N) ergänzen

6. Tests bauen und ausführen

# Testprogramm kompilieren
lyxc tests/altitude_test.lyx \
    -I build/ \
    --lint \
    -o build/altitude_test.elf

# Tests ausführen
./build/altitude_test.elf

Erwartete Ausgabe:

=== AltitudeMonitor — Testlauf ===
[PASS] TC-01: result.valid soll false sein
[PASS] TC-01: warning soll NOMINAL sein
[PASS] TC-01: confidence soll 0 sein
[PASS] TC-02: result.valid soll true sein
[PASS] TC-02: warning soll NOMINAL sein
[PASS] TC-02: confidence soll >= 70 sein
[PASS] TC-03: result soll valid sein
[PASS] TC-03: 10.000m soll NOMINAL sein
[PASS] TC-04: result soll valid sein
[PASS] TC-04: soll CAUTION sein
[PASS] TC-05: result soll valid sein
[PASS] TC-05: soll WARNING sein
[PASS] TC-06: result soll valid sein
[PASS] TC-06: soll CRITICAL sein
[PASS] TC-07: implausible pressure soll result.valid = false ergeben
[PASS] TC-08: implausible temperature soll result.valid = false ergeben
[PASS] TC-09: Boundary-Frame soll valid sein
[PASS] TC-10: MCDC: invalid-Flag dominiert
[PASS] TC-10: MCDC: confidence soll 0 sein
=== ALLE TESTS BESTANDEN (19/19) ===


7. Statische Analyse und Call-Graph

Die statische Analyse sucht nach Problemen, die Compiler und Tests allein nicht finden — unerreichbarer Code, potenzielle Null-Pointer, nicht initialisierte Variablen, Division durch null.

lyxc src/altitude.lyx \
    -I build/ \
    --static-analysis \
    2>&1 | tee evidence/static_analysis.log

Beispielausgabe:

[analysis] AltitudeMonitor — ProcessSensorFrame
  [OK] No unreachable code detected
  [OK] No null-pointer dereferences detected
  [OK] No uninitialized variables
  [OK] No division-by-zero paths
  [OK] All enum cases handled in ClassifyAltitude
[analysis] Completed — 0 issues

Call-Graph erzeugen (zeigt, welche Funktion welche aufruft — Pflichtnachweis für DO-178C):

lyxc src/altitude.lyx \
    -I build/ \
    --call-graph \
    -o evidence/call_graph.dot

# Visualisierung als PNG (benötigt Graphviz)
dot -Tpng evidence/call_graph.dot -o evidence/call_graph.png

Der Call-Graph zeigt u.a., dass ProcessSensorFrame genau drei interne Funktionen aufruft (PressureToAltitude, ComputeConfidence, ClassifyAltitude) und keine externen oder nicht validierten Funktionen — das ist für DAL-B eine Anforderung.


8. Stack-Report erstellen

Der Stack-Report dokumentiert den maximalen Stack-Verbrauch jeder Funktion auf dem gesamten Call-Graph. Er ist der Nachweis, dass kein Stack-Overflow auftreten kann.

lyxc src/altitude.lyx \
    -I build/ \
    2>&1 | tee evidence/stack_report.log

Beispielausgabe:

[stack] AltitudeMonitor
  ProcessSensorFrame      312 / 512  bytes  OK
    PressureToAltitude     96 / 128  bytes  OK
    ComputeConfidence       80 / 128  bytes  OK
    ClassifyAltitude        64 / 128  bytes  OK
  Worst-case call depth: ProcessSensorFrame → PressureToAltitude
  Worst-case stack total: 408 bytes
[stack] All limits satisfied — PASS


9. MC/DC-Coverage messen

MC/DC (Modified Condition/Decision Coverage) ist für DAL-A und DAL-B vorgeschrieben. Jede atomare Bedingung in einem booleschen Ausdruck muss nachweislich unabhängig voneinander den Gesamtausdruck beeinflussen.

<WRAP alert> Dieser Ablauf ist mit dem heutigen Compiler nicht durchführbar (#1524). –mcdc baut keine Instrumentierung ein — das Programm ist mit und ohne den Schalter byte-identisch. Es entsteht keine .coverage-Datei, und –mcdc-report erzeugt keinen HTML-Bericht. Auch –link gibt es nicht.

Der folgende Abschnitt beschreibt daher, was zu tun wäre, und darunter steht, was der Compiler heute tatsächlich liefert. </WRAP>

Schritt 1 — Strukturübersicht der Entscheidungspunkte:

lyxc src/altitude.lyx -I build/ --mcdc-report -o /dev/null

[MC/DC] Coverage structure analysis
[MC/DC] Functions analyzed: 4
[MC/DC] Total decision points: 3
[MC/DC]   ProcessSensorFrame: 1 decisions, 1 conditions → min 2 test cases
[MC/DC] Decision point details:
[MC/DC]   point #0 in 'ProcessSensorFrame': if (node 12)

Damit sind die Entscheidungspunkte aufgefunden. Die Zahl hinter „conditions„ ist unbrauchbar — sie lautet immer 1, auch bei a && b && c; Entscheidungen in einem return-Ausdruck fehlen ganz.

Schritt 2 — Testvektoren von Hand herleiten. Für eine Entscheidung mit n Bedingungen sind n+1 Testfälle nötig; die Tabelle im DO-178C-Guide zeigt das Verfahren für drei Bedingungen.

Schritt 3 — Überdeckung außerhalb des Compilers belegen: ein Testrahmen, der jede Bedingungskombination und deren Ergebnis mitschreibt, liefert den Nachweis. Der Compiler kann ihn nicht erbringen.

Ein Bericht wie der folgende ist derzeit nicht erzeugbar und steht hier nur, um das Ziel zu zeigen:

Coverage Report — AltitudeMonitor
══════════════════════════════════════════════════════════════
Function                   Line     Branch   MC/DC
──────────────────────────────────────────────────────────────
ProcessSensorFrame         100%     100%     100%   ✓
PressureToAltitude         100%     100%     100%   ✓
ComputeConfidence          100%     100%      88%   ✗  ← DAL-B: OK, DAL-A: nicht ausreichend
ClassifyAltitude           100%     100%     100%   ✓
──────────────────────────────────────────────────────────────
GESAMT                     100%     100%      97%
══════════════════════════════════════════════════════════════
DAL-B Anforderung: 100% MC/DC für alle @dal(B)-Funktionen — PASS

Was das Ergebnis bedeutet: ComputeConfidence hat 88 % MC/DC. Für DAL-B ist das kein Problem — die Funktion ist nicht mit @dal(B) annotiert. Wäre das Projekt DAL-A, müsste ein weiterer Testfall ergänzt werden, der die fehlende Kombination abdeckt.

MC/DC-Lücke schließen (DAL-A Beispiel)

Der Report zeigt, welche Bedingung fehlt:

[mcdc-gap] ComputeConfidence — Bedingung 'frame.valid' hat keinen Test, bei dem
           'p_ok && t_ok = true' aber 'frame.valid' allein das Ergebnis bestimmt.
           Fehlende Kombination: valid=true, p_ok=true, t_ok=true → nur valid=false dreht Ergebnis.

Zusätzlichen Testfall ergänzen:

// TC-11: MC/DC — valid=false bei sonst gültigen Werten
fn TC11_MCDC_ValidAlone(): void {
    var frame_valid   := MakeFrame(101325, 15, true);
    var frame_invalid := MakeFrame(101325, 15, false);
    var r_valid:   AltitudeResult := ProcessSensorFrame(frame_valid);
    var r_invalid: AltitudeResult := ProcessSensorFrame(frame_invalid);
    // Nur 'valid' unterscheidet sich — MC/DC-Nachweis für diese Bedingung
    test.Assert( r_valid.valid,   "TC-11: valid=true  → result.valid = true");
    test.Assert(!r_invalid.valid, "TC-11: valid=false → result.valid = false");
}


10. Provenance und Audit-Log

Für die Zertifizierung muss nachweisbar sein, dass der gelieferte Maschinencode aus dem geprüften Quellcode entstanden ist — bit-identisch, reproduzierbar.

# Reproduzierbaren Final-Build mit vollständigem Audit-Log erzeugen
lyxc src/altitude.lyx \
    -I build/ \
    --lint \
    --static-analysis \
    --provenance \
    --trace-passes \
    --target=arm64 \
    -o build/altitude_final.elf \
    2>&1 | tee evidence/provenance.log

–provenance schreibt für jede IR-Transformation einen signierten Eintrag ins Log:

[provenance] Source:    src/altitude.lyx  SHA256: a3f8...c291
[provenance] Unit:      build/types.lyu   SHA256: 7b21...e04a
[provenance] Pass 01:   parse             → AST
[provenance] Pass 02:   type-check        → typed AST
[provenance] Pass 03:   range-check       → annotated AST
[provenance] Pass 04:   lower             → IR
[provenance] Pass 05:   mcdc              → instrumented IR
[provenance] Pass 06:   codegen arm64     → ELF
[provenance] Output:    build/altitude_final.elf  SHA256: 19d4...f7a2
[provenance] Compiler:  lyxc 1.0.16F
[provenance] Flags:     --lint --static-analysis --provenance --trace-passes --target=arm64
[provenance] Timestamp: 2026-05-22T14:30:00Z

–build-info zeigt die Compiler-Version und Build-Konfiguration, die im Abnahmedokument festgehalten wird:

lyxc --build-info

Compiler Build Information:
  Name: lyxc
  Version: 1.0.19E
  Build: bootstrap
  Target: x86_64-linux-elf

Mehr als diese vier Angaben liefert der Schalter nicht — Baudatum, Reproduzierbarkeit und FIPS-Status, die frühere Fassungen dieses Tutorials zeigten, gibt es nicht. Für ein Abnahmedokument sind Datum und Zielplattform daher gesondert festzuhalten.

--config (TOR-003) ergänzt die Angaben um die verfügbaren Zielplattformen, Architekturen und Ausgabeformate. Der Schalter arbeitet seit der Behebung von #1527 korrekt und endet mit Exit 0 — ältere Fassungen dieses Tutorials rieten davon ab.


11. Nachweis-Checkliste nach DAL-Level

Die folgende Tabelle zeigt, welche Schritte aus diesem Tutorial für welches DAL-Level verpflichtend sind.

Nachweis DAL-A DAL-B DAL-C DAL-D
–lint (DO-178C-Konformitätsprüfung)
–static-analysis
@stack_limit (bei jeder Übersetzung geprüft)
–call-graph (Call-Graph-Dokumentation)
100 % MC/DC — ausserhalb des Compilers (#1524)
Statement Coverage 100 %
Branch Coverage 100 %
Traceability über –provenance (seit 1.0.20F nutzbar, #1523)
–trace-passes (Audit-Log)
@redundant (TMR) für Zustandsvariablen ✓ (empfohlen)
@integrity(mode: software_lockstep) ✓ (empfohlen)
Boundary Value Analysis in Tests
@dal-Annotation im Quellcode

12. Vollständiger Build-Ablauf (Skript)

Das folgende Skript führt alle Schritte in der richtigen Reihenfolge aus und legt alle Nachweise im evidence/-Verzeichnis ab. Es kann als Basis für eine CI/CD-Pipeline oder einen manuellen Abnahme-Build dienen.

#!/bin/bash
set -e   # Abbruch bei jedem Fehler

echo "=== AltitudeMonitor — Safety Build ==="

# 1. Typen-Unit kompilieren
lyxc src/types.lyx \
    --compile-unit \
    --lint \
    -o build/types.lyu

# 2. Hauptkomponente — Lint + Statische Analyse
lyxc src/altitude.lyx \
    -I build/ \
    --lint \
    --static-analysis \
    --call-graph -o evidence/call_graph.dot \
    --target=arm64 \
    -o build/altitude.elf \
    2>&1 | tee evidence/static_analysis.log

# 3. Tests bauen und ausführen
lyxc tests/altitude_test.lyx \
    -I build/ \
    --lint \
    -o build/altitude_test.elf

./build/altitude_test.elf

# 4. MC/DC-Instrumentierung und Coverage
lyxc src/altitude.lyx \
    -I build/ \
    --mcdc-report \
    --target=arm64 \
    -o /dev/null  > evidence/mcdc_struktur.log 2>&1

# Testprogramm getrennt uebersetzen — ein --link gibt es nicht,
# die Unit wird ueber -I eingebunden
lyxc tests/altitude_test.lyx \
    -I build/ \
    -o build/altitude_test.elf

./build/altitude_test.elf

# Eine Coverage-Datei entsteht dabei nicht (#1524) — der Nachweis
# ist ausserhalb des Compilers zu fuehren.

# 5. Final-Build mit Provenance
lyxc src/altitude.lyx \
    -I build/ \
    --lint \
    --static-analysis \
    --provenance \
    --trace-passes \
    --target=arm64 \
    -o build/altitude_final.elf \
    2>&1 | tee evidence/provenance.log

# 6. Call-Graph als PNG (optional, benötigt Graphviz)
if command -v dot &> /dev/null; then
    dot -Tpng evidence/call_graph.dot -o evidence/call_graph.png
fi

echo ""
echo "=== Build erfolgreich ==="
echo "Nachweise in evidence/:"
ls -lh evidence/


Zusammenfassung

Schritt Kommando Artefakt
Typen-Unit lyxc –compile-unit –lint build/types.lyu
Safety-Build –lint –static-analysis –runtime-checks build/altitude.elf
Tests ausführen ./build/altitude_test.elf Konsolenausgabe
Statische Analyse –static-analysis evidence/static_analysis.log
Call-Graph –call-graph evidence/call_graph.dot
Stack-Prüfung @stack_limit im Quelltext — kein Flag Compiler-Fehler bei Überschreitung
MC/DC-Coverage –mcdc + –mcdc-report Bericht auf stdout
Provenance –provenance –trace-passes evidence/provenance.log

Zurück: Aerospace & Safety – Sprachreferenz

Codebeispiele geprüft: gegen lyxc 1.2.5C übersetzt (Prüflauf 2026-09-08 über die gesamte Doku: 574 Vollprogramme, 0 echte Fehler; zusätzlich 5159 Aufrufe gegen die pub fn-Signaturen in aurum/std gehalten, 0 Abweichungen).


Letzte Aktualisierung: 2026-09-05 (#1908, gemessen mit lyxc 1.1.18A) — @integrity(mode: lockstep) in der Attributtabelle auf mode: software_lockstep berichtigt — lockstep ist kein gültiger Modus.