| @@ -0,0 +1,831 @@ | |||
| { | |||
| "document_type": "technical_concept", | |||
| "language": "de", | |||
| "audience": [ | |||
| "AI-Entwicklerteam", | |||
| "Reviewer", | |||
| "Maintainer" | |||
| ], | |||
| "project": { | |||
| "name": "fm-rds-tx", | |||
| "goal": "Aus dem bestehenden FM-Stereo/RDS-TX-System ein technisch sauberes, deterministisches, messbares und betriebsfestes Pro-Level-System machen.", | |||
| "primary_priority": "Technische Perfektion", | |||
| "secondary_priority": "Sinnvolle Umsetzungsreihenfolge mit maximalem Risikoabbau zuerst" | |||
| }, | |||
| "executive_summary": { | |||
| "current_strengths": [ | |||
| "Saubere Modultrennung zwischen Generator, DSP, Control, Audio, Backend und Plattform.", | |||
| "Persistenter DSP-Zustand ist bereits vorhanden, insbesondere im Generator und im FM-Upsampler.", | |||
| "Live-Updates werden bereits über atomare Snapshots bzw. Pointer modelliert.", | |||
| "Es existieren bereits viele Unit-Tests sowie spektrale Blackbox-Tests für 19 kHz, 38 kHz und 57 kHz." | |||
| ], | |||
| "current_limitations": [ | |||
| "Der TX-Pfad in internal/app/engine.go ist noch ein einzelner synchroner Generate/Upsample/Write-Loop ohne entkoppelten Echtzeitpuffer.", | |||
| "Die Runtime-Recovery ist schwach: bei Fehlern wird nur gezählt, geloggt und gewartet; es gibt keinen expliziten Fault-State mit deterministischem Fallback.", | |||
| "Die Control-Plane in internal/control/control.go ist funktional, aber nicht hart genug: keine Authentisierung, keine Transporthärtung, keine Request-Limits, keine Timeouts, keine Audit-Trails.", | |||
| "Validation und Runtime-Semantik sind nicht überall deckungsgleich; Beispiel: fm.outputDrive wird in config.Validate() bis 10 akzeptiert, in Engine.UpdateConfig() aber nur bis 3.", | |||
| "Device-Abstraktion ist brauchbar, aber noch nicht streng genug capability- und kalibrierungsgetrieben.", | |||
| "Observability ist noch zu schwach für echten Dauerbetrieb und reproduzierbare Fehleranalyse." | |||
| ], | |||
| "core_statement": "Der DSP-Kern ist nah an ernsthaft brauchbar. Der Abstand zu Pro-Level liegt primär in Betriebssicherheit, Observability, Hardwarevalidierung und strenger Runtime-Kontrolle." | |||
| }, | |||
| "repo_grounding": { | |||
| "confirmed_code_touchpoints": [ | |||
| { | |||
| "path": "internal/app/engine.go", | |||
| "observation": "TX läuft aktuell in einer einzelnen Goroutine als synchroner Zyklus: GenerateFrame -> optional FMUpsampler.Process -> driver.Write. Kein echter Producer/Consumer-Puffer zwischen Generator und Hardwarewriter." | |||
| }, | |||
| { | |||
| "path": "internal/offline/generator.go", | |||
| "observation": "Generator hat bereits persistenten Zustand, LiveParams per atomic.Pointer und sinnvolle DSP-Kette inklusive optionalem BS.412-Limiter." | |||
| }, | |||
| { | |||
| "path": "internal/control/control.go", | |||
| "observation": "HTTP-Control existiert bereits, aber ohne sichtbare Authentisierung, ohne Server-Timeout-Konfiguration und ohne harte API-Grenzen." | |||
| }, | |||
| { | |||
| "path": "internal/config/config.go", | |||
| "observation": "Validation ist vorhanden, aber semantisch nicht überall konsistent mit Laufzeitregeln." | |||
| }, | |||
| { | |||
| "path": "internal/platform/soapy.go", | |||
| "observation": "Capabilities und RuntimeStats existieren als Ansatz, reichen aber noch nicht für eine wirklich harte device-aware Steuerung." | |||
| }, | |||
| { | |||
| "path": "internal/audio/stream.go", | |||
| "observation": "Lock-freier SPSC-Ringbuffer für Live-Audio ist bereits vorhanden und kann als Referenz für deterministische Buffer-Designs dienen." | |||
| } | |||
| ], | |||
| "confirmed_inconsistencies": [ | |||
| { | |||
| "id": "CFG-SEM-001", | |||
| "description": "fm.outputDrive wird in Config.Validate() bis 10 akzeptiert, die Fehlermeldung spricht aber von 0..3, und Engine.UpdateConfig() erzwingt tatsächlich 0..3." | |||
| }, | |||
| { | |||
| "id": "CTL-UX-001", | |||
| "description": "handleAudioStream() nennt in der Fehlermeldung '--audio-http', im CLI ist dieser Schalter nicht als gleichwertiger offensichtlicher Bedienpfad bestätigt." | |||
| } | |||
| ] | |||
| }, | |||
| "design_principles": [ | |||
| "Kein verstecktes Glück: Jeder relevante Echtzeit- oder RF-Pfad muss deterministisch, messbar und reproduzierbar sein.", | |||
| "Fail-safe statt fail-weird: Bei Unsicherheit oder Überlast lieber definiert muten oder faulten als kaputt weiterzusenden.", | |||
| "Eine Runtime-Wahrheit: Konfiguration, Live-State und tatsächlich angewandte Hardware-/DSP-Parameter dürfen nicht auseinanderlaufen.", | |||
| "Hardware ist Wahrheit: IQ-Dateien und Unit-Tests reichen nicht; es braucht Hardware-in-the-loop und externe Decoder-/Messvalidierung.", | |||
| "Observability ist Pflicht, nicht Luxus: Kein Pro-Level ohne Metriken, strukturierte Logs, Fault-Telemetrie und reproduzierbare Regressionen.", | |||
| "Keine implizite Semantik: Alle Parameter müssen in Config, API, Runtime und Telemetrie exakt dasselbe bedeuten." | |||
| ], | |||
| "priority_model": { | |||
| "P0": "Technische Perfektion und Determinismus", | |||
| "P1": "Betriebssicherheit und Fehlerbeherrschung", | |||
| "P2": "Hardware-Wahrheit und RF-Qualität", | |||
| "P3": "Sichere und saubere Runtime-Steuerung", | |||
| "P4": "Deployment-, Release- und Service-Reife" | |||
| }, | |||
| "workstreams": [ | |||
| { | |||
| "id": "WS-01", | |||
| "priority": "P0", | |||
| "title": "Deterministische Echtzeit-TX-Pipeline mit entkoppeltem Writer", | |||
| "why": "Der aktuelle Single-Loop ist elegant, aber anfällig gegen Write-Spikes, Scheduling-Jitter und hardwarebedingte Blockaden. Pro-Level verlangt Entkopplung zwischen Erzeugung und Ausgabe.", | |||
| "objective": "Generator/Upsampler und Hardwarewriter werden als getrennte Stufen mit kleinem, kontrolliertem Frame-Puffer betrieben. Der Writer darf kurzfristige Timing-Jitter absorbieren, ohne sofort den gesamten TX-Zyklus zu ruinieren.", | |||
| "target_architecture": { | |||
| "pipeline": [ | |||
| "control-plane", | |||
| "runtime supervisor", | |||
| "generator worker", | |||
| "optional upsampler worker", | |||
| "bounded frame queue", | |||
| "writer worker", | |||
| "driver" | |||
| ], | |||
| "queue_policy": { | |||
| "type": "bounded ring queue", | |||
| "capacity_frames": 3, | |||
| "default_behavior": "Producer füllt vor, Writer sendet in Echtzeit, Supervisor überwacht Queue-Füllstand", | |||
| "allowed_strategies": [ | |||
| "block producer kurzzeitig", | |||
| "repeat last safe frame im Fault-Recovery-Modus", | |||
| "mute frame im Sicherheitsmodus" | |||
| ], | |||
| "forbidden_strategies": [ | |||
| "unbounded buffering", | |||
| "still und heimlich alte Frames verwerfen ohne Counter/Log", | |||
| "dynamisches Verhalten ohne Telemetrie" | |||
| ] | |||
| } | |||
| }, | |||
| "implementation_tasks": [ | |||
| { | |||
| "id": "WS-01-T1", | |||
| "title": "FrameQueue einführen", | |||
| "details": [ | |||
| "Neue interne Queue-Struktur für CompositeFrame oder DeviceFrame einführen.", | |||
| "Explizit festlegen, ob vor oder nach FMUpsampler gepuffert wird. Empfehlung: Puffern auf Device-Frame-Ebene, damit der Writer nur noch sendet.", | |||
| "FrameQueue muss feste Kapazität, Füllstand, High-Watermark, Low-Watermark und Drop-/Mute-/Repeat-Counter liefern." | |||
| ] | |||
| }, | |||
| { | |||
| "id": "WS-01-T2", | |||
| "title": "Writer-Worker einführen", | |||
| "details": [ | |||
| "Writer läuft in eigener Goroutine und besitzt alleinige Ownership über driver.Write().", | |||
| "Nur der Writer darf Write- und Tune-nahe Timinginteraktionen mit dem Treiber koordinieren.", | |||
| "Write-Dauer, Blockzeiten und Late-Events werden pro Frame gemessen." | |||
| ] | |||
| }, | |||
| { | |||
| "id": "WS-01-T3", | |||
| "title": "Supervisor-Schicht einführen", | |||
| "details": [ | |||
| "Supervisor bewertet Queue-Füllstand, Late-Rate, Fehlerhäufigkeit und entscheidet über Normal/Fault/Recovery.", | |||
| "Supervisor ist nicht nur Logik, sondern Runtime-State-Maschine." | |||
| ] | |||
| } | |||
| ], | |||
| "acceptance_criteria": [ | |||
| "Keine direkte synchrone Generate->Write-Kopplung mehr im Hauptpfad.", | |||
| "Queue-Füllstand und Write-Latenz sind live sichtbar.", | |||
| "Kurzzeitige Write-Spikes führen nicht sofort zu hörbaren Aussetzern oder unkontrolliertem Timing-Kollaps.", | |||
| "Langlauf mit 6h Testdauer zeigt keine wachsende Drift im Kontrollpfad und keine ungebremste Fehlereskalation." | |||
| ], | |||
| "affected_files": [ | |||
| "internal/app/engine.go", | |||
| "internal/output/backend.go", | |||
| "internal/platform/soapy.go" | |||
| ], | |||
| "example_interfaces": { | |||
| "frame_queue_contract": "type FrameQueue interface { Push(frame *output.CompositeFrame) error; Pop(ctx context.Context) (*output.CompositeFrame, error); FillLevel() float64; Depth() int; Capacity() int; Stats() QueueStats }", | |||
| "queue_stats_example": { | |||
| "capacity": 3, | |||
| "depth": 2, | |||
| "fillLevel": 0.6667, | |||
| "pushTimeouts": 0, | |||
| "popTimeouts": 0, | |||
| "droppedFrames": 0, | |||
| "repeatedFrames": 0, | |||
| "mutedFrames": 0 | |||
| } | |||
| } | |||
| }, | |||
| { | |||
| "id": "WS-02", | |||
| "priority": "P0", | |||
| "title": "Explizite Runtime-State-Maschine und Fault-Handling", | |||
| "why": "Aktuell existieren im Engine-State nur idle, running, stopping. Das reicht nicht für professionelles Fehlermanagement im Sendebetrieb.", | |||
| "objective": "Einführen eines klaren Betriebsmodells mit Fault-, Recovery- und Muted-Zuständen. Jeder kritische Fehlerpfad endet in definiertem Verhalten.", | |||
| "target_state_machine": { | |||
| "states": [ | |||
| "idle", | |||
| "arming", | |||
| "prebuffering", | |||
| "running", | |||
| "degraded", | |||
| "muted", | |||
| "faulted", | |||
| "stopping" | |||
| ], | |||
| "transition_examples": [ | |||
| { | |||
| "from": "idle", | |||
| "to": "arming", | |||
| "trigger": "StartTX angefordert und Grundvalidierung erfolgreich" | |||
| }, | |||
| { | |||
| "from": "arming", | |||
| "to": "prebuffering", | |||
| "trigger": "Driver gestartet, Queue erstellt, Generator bereit" | |||
| }, | |||
| { | |||
| "from": "prebuffering", | |||
| "to": "running", | |||
| "trigger": "Queue-Minimum erreicht" | |||
| }, | |||
| { | |||
| "from": "running", | |||
| "to": "degraded", | |||
| "trigger": "Late-Rate oberhalb Schwellwert oder Queue-Füllstand wiederholt kritisch" | |||
| }, | |||
| { | |||
| "from": "degraded", | |||
| "to": "muted", | |||
| "trigger": "Writer kann keine sichere Ausgabe mehr garantieren" | |||
| }, | |||
| { | |||
| "from": "muted", | |||
| "to": "faulted", | |||
| "trigger": "Persistenter Treiberfehler oder Recovery gescheitert" | |||
| }, | |||
| { | |||
| "from": "muted", | |||
| "to": "running", | |||
| "trigger": "Queue und Treiber wieder stabil" | |||
| } | |||
| ] | |||
| }, | |||
| "implementation_tasks": [ | |||
| { | |||
| "id": "WS-02-T1", | |||
| "title": "Fault-Klassifikation definieren", | |||
| "details": [ | |||
| "Treiberfehler", | |||
| "Write-Time-Budget überschritten", | |||
| "Queue leer", | |||
| "Queue permanent überfüllt", | |||
| "Signal-Selbsttest fehlgeschlagen", | |||
| "unerlaubte Live-Konfigurationsänderung" | |||
| ] | |||
| }, | |||
| { | |||
| "id": "WS-02-T2", | |||
| "title": "Reaktionsstrategie pro Fehlerklasse definieren", | |||
| "details": [ | |||
| "Warnen ohne Zustandswechsel", | |||
| "degraded mit Countern", | |||
| "muted mit stiller Trägerstrategie oder kompletter TX-Stille", | |||
| "faulted mit manueller oder automatischer Recovery" | |||
| ] | |||
| }, | |||
| { | |||
| "id": "WS-02-T3", | |||
| "title": "Event-Log und Fault-Historie einführen", | |||
| "details": [ | |||
| "Jeder Zustandswechsel ist auditierbar.", | |||
| "Faults enthalten Ursache, Timestamp, Metriken und letzten bekannten Runtime-Kontext." | |||
| ] | |||
| } | |||
| ], | |||
| "acceptance_criteria": [ | |||
| "Jede kritische Fehlersituation führt in einen expliziten Zustand statt in implizites Weiterlaufen.", | |||
| "Der aktuelle Runtime-State ist über API und Telemetrie jederzeit sichtbar.", | |||
| "Degraded/Muted/Faulted lassen sich in Tests gezielt triggern und verifizieren." | |||
| ], | |||
| "affected_files": [ | |||
| "internal/app/engine.go", | |||
| "internal/control/control.go" | |||
| ], | |||
| "example_fault_policy": { | |||
| "late_buffer_threshold_per_60s": 10, | |||
| "queue_critical_fill_below": 0.1, | |||
| "writer_error_burst": 3, | |||
| "policy": [ | |||
| "Bei 1-2 Einzelereignissen nur Counter erhöhen.", | |||
| "Ab Burstschwelle auf degraded schalten.", | |||
| "Wenn degraded > 5s anhält oder Write wiederholt fehlschlägt -> muted.", | |||
| "Wenn muted nicht innerhalb definierter Frist stabilisiert werden kann -> faulted." | |||
| ] | |||
| } | |||
| }, | |||
| { | |||
| "id": "WS-03", | |||
| "priority": "P0", | |||
| "title": "Semantische Korrektheit und harte Config-/Runtime-Konsistenz", | |||
| "why": "Technische Perfektion scheitert oft nicht am DSP, sondern an stillen Semantikabweichungen zwischen Config, API, Live-Update und tatsächlicher Laufzeit.", | |||
| "objective": "Ein einziger, eindeutig definierter Parameterraum. Jeder Wert hat exakt eine Bedeutung und identische Constraints in Config, HTTP-API, Runtime und Telemetrie.", | |||
| "implementation_tasks": [ | |||
| { | |||
| "id": "WS-03-T1", | |||
| "title": "Parameterinventar erstellen", | |||
| "details": [ | |||
| "Alle öffentlich und intern verwendeten Parameter inventarisieren.", | |||
| "Für jeden Parameter: Typ, Einheit, Bereich, Default, Hot-Reload-Fähigkeit, Safety-Relevanz, Telemetrie-Name." | |||
| ] | |||
| }, | |||
| { | |||
| "id": "WS-03-T2", | |||
| "title": "Validation vereinheitlichen", | |||
| "details": [ | |||
| "Config.Validate(), Engine.UpdateConfig() und API-Patch-Validierung dürfen nicht divergieren.", | |||
| "Beispiel: outputDrive muss an allen Stellen denselben Bereich haben." | |||
| ] | |||
| }, | |||
| { | |||
| "id": "WS-03-T3", | |||
| "title": "AppliedConfig einführen", | |||
| "details": [ | |||
| "Neben DesiredConfig muss es eine AppliedConfig geben.", | |||
| "API-Antworten sollen nicht nur sagen, was gewünscht wurde, sondern was tatsächlich übernommen wurde." | |||
| ] | |||
| } | |||
| ], | |||
| "acceptance_criteria": [ | |||
| "Kein Parameter hat an zwei Stellen unterschiedliche Grenzwerte oder Einheiten.", | |||
| "API kann DesiredConfig und AppliedConfig getrennt zurückgeben.", | |||
| "Ungültige Hot-Updates werden deterministisch abgelehnt und nicht teilweise angewandt." | |||
| ], | |||
| "affected_files": [ | |||
| "internal/config/config.go", | |||
| "internal/app/engine.go", | |||
| "internal/control/control.go" | |||
| ], | |||
| "example_parameter_schema": { | |||
| "name": "fm.outputDrive", | |||
| "unit": "logical composite drive factor", | |||
| "type": "float64", | |||
| "range": { | |||
| "min": 0.0, | |||
| "max": 3.0 | |||
| }, | |||
| "default": 1.0, | |||
| "hot_reloadable": true, | |||
| "safety_class": "medium", | |||
| "notes": "Darf nicht mit hardware gain oder RF gain verwechselt werden." | |||
| } | |||
| }, | |||
| { | |||
| "id": "WS-04", | |||
| "priority": "P1", | |||
| "title": "Observability, Telemetrie und Diagnosefähigkeit", | |||
| "why": "Ohne harte Telemetrie bleibt jedes Timing- oder RF-Problem ratenbasiert. Pro-Level braucht Metriken, strukturierte Logs und Diagnostik-Endpunkte.", | |||
| "objective": "Vollständige Sichtbarkeit auf Runtime, Queue, Writer, Generator, RF-Selbsttests und API-Aktivität schaffen.", | |||
| "implementation_tasks": [ | |||
| { | |||
| "id": "WS-04-T1", | |||
| "title": "Strukturiertes Logging einführen", | |||
| "details": [ | |||
| "Einheitliches Logging-Backend nutzen.", | |||
| "Keine verstreuten Printf-only Pfade als primäre Diagnose.", | |||
| "Jeder Fault, State-Change und API-Eingriff wird strukturiert geloggt." | |||
| ] | |||
| }, | |||
| { | |||
| "id": "WS-04-T2", | |||
| "title": "Prometheus-kompatible Metriken einführen", | |||
| "details": [ | |||
| "Engine-Metriken", | |||
| "Writer-Metriken", | |||
| "Queue-Metriken", | |||
| "RDS-/Pilot-Selbsttest-Metriken", | |||
| "Audio-Stream-Metriken", | |||
| "Control-Plane-Metriken" | |||
| ] | |||
| }, | |||
| { | |||
| "id": "WS-04-T3", | |||
| "title": "Debug- und Profiling-Endpunkte", | |||
| "details": [ | |||
| "pprof optional aktivierbar", | |||
| "Build-Info, Git-Commit, Build-Tags, Backend, Plattform und Runtime-Version ausgeben" | |||
| ] | |||
| } | |||
| ], | |||
| "acceptance_criteria": [ | |||
| "Jeder relevante Betriebsaspekt ist per Runtime-Endpunkt oder Metrics-Endpunkt sichtbar.", | |||
| "Fehlerfälle sind anhand von Logs und Countern nachvollziehbar, ohne den Code erneut zu lesen.", | |||
| "Langlaufprobleme lassen sich zeitlich korrelieren." | |||
| ], | |||
| "example_metrics": [ | |||
| "engine_chunks_generated_total", | |||
| "engine_late_buffers_total", | |||
| "engine_fault_transitions_total", | |||
| "writer_write_duration_seconds", | |||
| "queue_fill_ratio", | |||
| "queue_dropped_frames_total", | |||
| "queue_muted_frames_total", | |||
| "driver_write_errors_total", | |||
| "audio_stream_underruns_total", | |||
| "audio_stream_overflows_total", | |||
| "rf_selftest_pilot_db", | |||
| "rf_selftest_rds_57k_db" | |||
| ], | |||
| "affected_files": [ | |||
| "internal/app/engine.go", | |||
| "internal/control/control.go", | |||
| "internal/platform/soapy.go", | |||
| "internal/audio/stream.go" | |||
| ] | |||
| }, | |||
| { | |||
| "id": "WS-05", | |||
| "priority": "P1", | |||
| "title": "Sichere und erwachsene Control-Plane", | |||
| "why": "Sobald TX start/stop, Frequenz, RDS-Text oder Live-Audio per HTTP steuerbar sind, ist die Control-Plane ein sicherheitsrelevanter Teil des Systems.", | |||
| "objective": "API transport- und anwendungsseitig härten, state-aware machen und auditierbar gestalten.", | |||
| "implementation_tasks": [ | |||
| { | |||
| "id": "WS-05-T1", | |||
| "title": "Auth und Deploy-Modi definieren", | |||
| "details": [ | |||
| "Mindestens Token-Auth für Remote-Betrieb.", | |||
| "Optionale mTLS-Unterstützung für geschützte Infrastrukturen.", | |||
| "Explizite Betriebsmodi: localhost-only, trusted-lan, secured-remote." | |||
| ] | |||
| }, | |||
| { | |||
| "id": "WS-05-T2", | |||
| "title": "HTTP-Server härten", | |||
| "details": [ | |||
| "ReadTimeout", | |||
| "WriteTimeout", | |||
| "IdleTimeout", | |||
| "ReadHeaderTimeout", | |||
| "Body-Size-Limits", | |||
| "Content-Type-Validierung", | |||
| "Method enforcement" | |||
| ] | |||
| }, | |||
| { | |||
| "id": "WS-05-T3", | |||
| "title": "API semantisch aufräumen", | |||
| "details": [ | |||
| "DesiredConfig vs AppliedConfig vs RuntimeState", | |||
| "Idempotente Start/Stop-Endpunkte", | |||
| "Transaktionsartige Apply-/Reject-Antworten für Patches", | |||
| "Audit-Log pro wirksamem Eingriff" | |||
| ] | |||
| } | |||
| ], | |||
| "acceptance_criteria": [ | |||
| "Kein ungeschützter Remote-TX-Betrieb im Standardmodus.", | |||
| "API liefert deterministische und vollständige Antworten.", | |||
| "Große oder falsche Requests können das System nicht unkontrolliert stressen." | |||
| ], | |||
| "affected_files": [ | |||
| "internal/control/control.go", | |||
| "cmd/fmrtx/main.go" | |||
| ], | |||
| "example_patch_response": { | |||
| "ok": true, | |||
| "requestedChangeId": "cfg-2026-04-05T12:00:00Z-0001", | |||
| "desired": { | |||
| "frequencyMHz": 100.2, | |||
| "ps": "JAN FM" | |||
| }, | |||
| "applied": { | |||
| "frequencyMHz": 100.2, | |||
| "ps": "JAN FM" | |||
| }, | |||
| "state": "running", | |||
| "warnings": [] | |||
| } | |||
| }, | |||
| { | |||
| "id": "WS-06", | |||
| "priority": "P2", | |||
| "title": "Hardware-in-the-loop und externe RF-Wahrheitsprüfung", | |||
| "why": "Ein DSP-System ist erst dann pro-tauglich, wenn es auf echter Hardware und mit externem Decoder/Messergebnis wiederholt korrekt ist.", | |||
| "objective": "Nightly- oder manuell triggerbare Hardware-Regressionen etablieren, die nicht nur intern, sondern extern prüfen, was tatsächlich gesendet wird.", | |||
| "implementation_tasks": [ | |||
| { | |||
| "id": "WS-06-T1", | |||
| "title": "Loopback-/Capture-Setup definieren", | |||
| "details": [ | |||
| "Referenz-SDR oder definierter externer Empfänger als Capture-Seite.", | |||
| "Leistungsschutz und Dummy-Load-Konzept klar dokumentieren.", | |||
| "Standard-Testfrequenz, Standard-Device-Rate und Standard-Testdauer festlegen." | |||
| ] | |||
| }, | |||
| { | |||
| "id": "WS-06-T2", | |||
| "title": "Automatisierte Analyse bauen", | |||
| "details": [ | |||
| "Pilotenergie 19 kHz", | |||
| "Stereoenergie 38 kHz", | |||
| "RDS-Energie 57 kHz", | |||
| "Deviation/Composite-Level", | |||
| "Trägergenauigkeit", | |||
| "RDS-Decodierbarkeit und Fehlerrate", | |||
| "Langlaufdrift" | |||
| ] | |||
| }, | |||
| { | |||
| "id": "WS-06-T3", | |||
| "title": "Externen Decoder in Regression einbinden", | |||
| "details": [ | |||
| "PS und RT müssen extern decodierbar und stabil sein.", | |||
| "Nicht nur intern erzeugte Bits prüfen." | |||
| ] | |||
| } | |||
| ], | |||
| "acceptance_criteria": [ | |||
| "Definierte Testsignale werden extern reproduzierbar korrekt empfangen.", | |||
| "Pilot-, Stereo- und RDS-Komponenten liegen mit stabilen Pegeln an den erwarteten Frequenzen.", | |||
| "Langlauftests zeigen keine schleichende Entwertung der Sendekette." | |||
| ], | |||
| "affected_files": [ | |||
| "internal/offline/spectral_test.go", | |||
| "internal/platform/soapy.go", | |||
| "scripts/*" | |||
| ], | |||
| "example_hil_report": { | |||
| "device": "pluto", | |||
| "testDurationMinutes": 30, | |||
| "pilot19kDetected": true, | |||
| "stereo38kDetected": true, | |||
| "rds57kDetected": true, | |||
| "rdsDecodeSuccessRate": 0.998, | |||
| "maxCarrierErrorHz": 12.0, | |||
| "maxLateBuffers": 0, | |||
| "result": "pass" | |||
| } | |||
| }, | |||
| { | |||
| "id": "WS-07", | |||
| "priority": "P2", | |||
| "title": "Device-aware Capability- und Kalibrierungsmodell", | |||
| "why": "Ein generisches Backend-Modell reicht nicht, wenn unterschiedliche SDRs unterschiedliche Raten, Gains, Latenzen und Abweichungen haben.", | |||
| "objective": "Pro Gerät bzw. Gerätetyp bekannte Fähigkeiten und Kalibrierungen explizit abbilden.", | |||
| "implementation_tasks": [ | |||
| { | |||
| "id": "WS-07-T1", | |||
| "title": "Capabilities ausbauen", | |||
| "details": [ | |||
| "Sample-Rate-Sets oder Bereiche", | |||
| "Gain-Semantik", | |||
| "Frequenzraster", | |||
| "MTU-/Buffer-Empfehlungen", | |||
| "Minimale stabile Chunkgrößen", | |||
| "Tune-Latenzverhalten" | |||
| ] | |||
| }, | |||
| { | |||
| "id": "WS-07-T2", | |||
| "title": "Kalibrierungsprofil einführen", | |||
| "details": [ | |||
| "frequencyOffsetHz", | |||
| "deviationScale", | |||
| "mpxGainCalibration", | |||
| "driverChunkRecommendation", | |||
| "safeDefaultGain" | |||
| ] | |||
| }, | |||
| { | |||
| "id": "WS-07-T3", | |||
| "title": "Device-aware Validation", | |||
| "details": [ | |||
| "Config nicht nur gegen generische Regeln validieren, sondern gegen bekannte Device-Fähigkeiten.", | |||
| "Nicht unterstützte Raten oder gefährliche Kombinationen früh blockieren." | |||
| ] | |||
| } | |||
| ], | |||
| "acceptance_criteria": [ | |||
| "Treiber und Runtime wissen explizit, was das konkrete Device sicher kann.", | |||
| "Kalibrierte Geräte liefern reproduzierbarere RF-Ergebnisse.", | |||
| "Fehlkonfigurationen werden vor TX-Beginn erkannt." | |||
| ], | |||
| "affected_files": [ | |||
| "internal/platform/soapy.go", | |||
| "internal/config/config.go" | |||
| ], | |||
| "example_calibration_profile": { | |||
| "deviceId": "pluto-serial-1234", | |||
| "sampleRateHz": 1000000, | |||
| "frequencyOffsetHz": -18.0, | |||
| "deviationScale": 0.97, | |||
| "mpxGainCalibration": 1.03, | |||
| "safeDefaultGainDb": -20.0, | |||
| "preferredChunkMs": 50 | |||
| } | |||
| }, | |||
| { | |||
| "id": "WS-08", | |||
| "priority": "P2", | |||
| "title": "Signal-Selbstüberwachung im Betrieb", | |||
| "why": "Nur Post-mortem-Messungen reichen nicht. Das System soll im Betrieb merken, wenn Pilot, RDS oder Composite auffällig werden.", | |||
| "objective": "Leichtgewichtige In-Band-Selbsttests auf Chunk-Basis oder in Intervallen ausführen und in Fault-Logik einspeisen.", | |||
| "implementation_tasks": [ | |||
| { | |||
| "id": "WS-08-T1", | |||
| "title": "Chunk-basierte RF-Checks definieren", | |||
| "details": [ | |||
| "Goertzel oder kleine FFT für 19 kHz, 38 kHz und 57 kHz", | |||
| "Composite-Clipping- und Pegelindikatoren", | |||
| "Optional Deviation-Schätzer" | |||
| ] | |||
| }, | |||
| { | |||
| "id": "WS-08-T2", | |||
| "title": "Anomalieerkennung definieren", | |||
| "details": [ | |||
| "Pilot fehlt", | |||
| "RDS ungewöhnlich schwach", | |||
| "unerwartete Composite-Energieverteilung", | |||
| "langsame Drift" | |||
| ] | |||
| } | |||
| ], | |||
| "acceptance_criteria": [ | |||
| "Runtime kann relevante RF-Anomalien erkennen und melden.", | |||
| "Selbsttests sind ausreichend billig, um die Echtzeitfähigkeit nicht zu gefährden." | |||
| ], | |||
| "affected_files": [ | |||
| "internal/dsp/goertzel.go", | |||
| "internal/offline/generator.go", | |||
| "internal/app/engine.go" | |||
| ] | |||
| }, | |||
| { | |||
| "id": "WS-09", | |||
| "priority": "P3", | |||
| "title": "Teststrategie von Unit-Tests zu echter Qualitätsabsicherung erweitern", | |||
| "why": "Viele Tests sind gut. Die richtigen Testklassen sind besser. Pro-Level verlangt deterministische Regressionen, Fuzzing und Concurrency-Tests.", | |||
| "objective": "Testpyramide so ausbauen, dass Signal, Runtime und API gleichermaßen abgesichert sind.", | |||
| "implementation_tasks": [ | |||
| { | |||
| "id": "WS-09-T1", | |||
| "title": "Golden-Vector-Tests", | |||
| "details": [ | |||
| "Definierte Inputsignale und erwartete Analysewerte festschreiben.", | |||
| "Nicht nur boolsche Pass/Fail-Aussagen, sondern tolerierte numerische Fenster." | |||
| ] | |||
| }, | |||
| { | |||
| "id": "WS-09-T2", | |||
| "title": "Long-run Regressionen", | |||
| "details": [ | |||
| "Tausende Chunks durchlaufen lassen.", | |||
| "Boundary-Continuity, Drift, Queue-Stabilität, Writer-Stabilität prüfen." | |||
| ] | |||
| }, | |||
| { | |||
| "id": "WS-09-T3", | |||
| "title": "Race Detector, Fuzzing und API-Mutation-Tests", | |||
| "details": [ | |||
| "Konfigurationspatches", | |||
| "RDS-Texte", | |||
| "Audio-Ingest", | |||
| "Start/Stop-Rennen", | |||
| "gleichzeitige Live-Updates" | |||
| ] | |||
| } | |||
| ], | |||
| "acceptance_criteria": [ | |||
| "Es gibt Regressionen für DSP, Runtime, API und HIL.", | |||
| "Race Detector und Fuzzing finden keine bekannten kritischen Pfade mehr.", | |||
| "Nightly-Regressions geben maschinenlesbare Berichte aus." | |||
| ] | |||
| }, | |||
| { | |||
| "id": "WS-10", | |||
| "priority": "P4", | |||
| "title": "Service-Reife, Packaging und Reproduzierbarkeit", | |||
| "why": "Ein System ist nicht professionell, wenn nur der Autor es zuverlässig starten kann.", | |||
| "objective": "Saubere Build-, Release- und Betriebsartefakte bereitstellen.", | |||
| "implementation_tasks": [ | |||
| { | |||
| "id": "WS-10-T1", | |||
| "title": "Reproduzierbare Builds", | |||
| "details": [ | |||
| "Build-Flags, Version, Commit und Tags ins Binary schreiben.", | |||
| "Artefakte pro Zielplattform konsistent erzeugen." | |||
| ] | |||
| }, | |||
| { | |||
| "id": "WS-10-T2", | |||
| "title": "Service-Units und Beispiel-Deployments", | |||
| "details": [ | |||
| "systemd unit", | |||
| "EnvironmentFile-Unterstützung", | |||
| "Beispielkonfigurationen pro Backend" | |||
| ] | |||
| }, | |||
| { | |||
| "id": "WS-10-T3", | |||
| "title": "Migrations- und Schema-Versionierung", | |||
| "details": [ | |||
| "Config-Versionen einführen.", | |||
| "Klare Migrationsstrategie für ältere JSON-Konfigurationen." | |||
| ] | |||
| } | |||
| ], | |||
| "acceptance_criteria": [ | |||
| "Standardisierte Start- und Betriebswege existieren.", | |||
| "Build- und Runtime-Version sind eindeutig sichtbar.", | |||
| "Alte Konfigurationen können kontrolliert migriert werden." | |||
| ] | |||
| } | |||
| ], | |||
| "implementation_sequence": [ | |||
| { | |||
| "order": 1, | |||
| "workstreams": [ | |||
| "WS-03", | |||
| "WS-01", | |||
| "WS-02" | |||
| ], | |||
| "reason": "Erst muss die Semantik stimmen, dann die deterministische Pipeline, dann das Fault-Modell. Sonst baut man saubere Infrastruktur auf falschen Annahmen." | |||
| }, | |||
| { | |||
| "order": 2, | |||
| "workstreams": [ | |||
| "WS-04", | |||
| "WS-05" | |||
| ], | |||
| "reason": "Sobald Runtime-Struktur sauber ist, müssen Sichtbarkeit und sichere Steuerung folgen. Sonst ist der neue Kern schwer überprüfbar und riskant bedienbar." | |||
| }, | |||
| { | |||
| "order": 3, | |||
| "workstreams": [ | |||
| "WS-06", | |||
| "WS-07", | |||
| "WS-08" | |||
| ], | |||
| "reason": "Jetzt wird die Hardware-Wahrheit und RF-Qualität fest verdrahtet: HIL, Kalibrierung und laufende Signalkontrolle." | |||
| }, | |||
| { | |||
| "order": 4, | |||
| "workstreams": [ | |||
| "WS-09", | |||
| "WS-10" | |||
| ], | |||
| "reason": "Zum Schluss werden Regressionstiefe, Packaging und Service-Reife maximal professionalisiert." | |||
| } | |||
| ], | |||
| "cross_cutting_rules": { | |||
| "musts": [ | |||
| "Jeder neue Runtime-Zustand muss per API und Telemetrie sichtbar sein.", | |||
| "Jede neue Recovery- oder Drop-/Mute-Strategie braucht Counter, Logs und Tests.", | |||
| "Keine neue Konfigurationsoption ohne klaren Typ, Bereich, Einheit, Default und Hot-Reload-Klassifikation.", | |||
| "Hardware-nahe Änderungen brauchen mindestens Simulations- und HIL-Validierung.", | |||
| "Alle Faults müssen eine maschinenlesbare Ursache und eine menschenlesbare Zusammenfassung haben." | |||
| ], | |||
| "must_not": [ | |||
| "Keine unbounded Queues.", | |||
| "Keine stillen Fallbacks ohne Telemetrie.", | |||
| "Keine teilweise angewandten Live-Config-Änderungen ohne explizite Rückmeldung.", | |||
| "Keine unterschiedlichen Grenzwerte zwischen Config, API und Runtime.", | |||
| "Keine sicherheitsrelevanten HTTP-Endpunkte ohne Härtung im Remote-Betrieb." | |||
| ] | |||
| }, | |||
| "concrete_examples": { | |||
| "example_runtime_status": { | |||
| "state": "degraded", | |||
| "substate": "writer_backpressure", | |||
| "engine": { | |||
| "chunksProduced": 128443, | |||
| "lateBuffers": 7, | |||
| "underruns": 0, | |||
| "maxCycleMs": 51.12, | |||
| "maxWriteMs": 49.91 | |||
| }, | |||
| "queue": { | |||
| "capacity": 3, | |||
| "depth": 1, | |||
| "fillLevel": 0.3333, | |||
| "droppedFrames": 0, | |||
| "repeatedFrames": 2, | |||
| "mutedFrames": 0 | |||
| }, | |||
| "driver": { | |||
| "txEnabled": true, | |||
| "streamActive": true, | |||
| "samplesWritten": 64221500, | |||
| "slowWrites": 4 | |||
| }, | |||
| "lastFault": { | |||
| "code": "WRITER-LATE-BURST", | |||
| "at": "2026-04-05T12:34:56Z", | |||
| "message": "Write-Latenz wiederholt über Chunk-Budget" | |||
| } | |||
| }, | |||
| "example_fault_event": { | |||
| "eventId": "fault-000184", | |||
| "severity": "error", | |||
| "stateBefore": "running", | |||
| "stateAfter": "muted", | |||
| "code": "QUEUE-EMPTY-RECOVERY-FAILED", | |||
| "message": "Queue blieb trotz Recovery leer; Ausgang wurde auf Mute gesetzt", | |||
| "metrics": { | |||
| "queueFillLevel": 0.0, | |||
| "lateBuffersLast60s": 14, | |||
| "writerErrorsLast60s": 3 | |||
| } | |||
| }, | |||
| "example_rollout_plan": { | |||
| "milestone_1": "Semantik und State-Maschine stabil", | |||
| "milestone_2": "Entkoppelter Writer mit Telemetrie", | |||
| "milestone_3": "Sichere API und Audit-Log", | |||
| "milestone_4": "HIL-Regression und Kalibrierung", | |||
| "milestone_5": "Nightly-Qualitätsgates und reproduzierbare Releases" | |||
| } | |||
| }, | |||
| "definition_of_done": { | |||
| "technical": [ | |||
| "TX-Pfad ist deterministisch entkoppelt und fault-tolerant.", | |||
| "DesiredConfig, AppliedConfig und RuntimeState sind sauber getrennt.", | |||
| "Alle kritischen Fehler führen in explizite Zustände.", | |||
| "RF-Signalqualität ist intern und extern nachweisbar.", | |||
| "Metriken, strukturierte Logs und Diagnosepfade sind vollständig." | |||
| ], | |||
| "operational": [ | |||
| "System läuft im Langlauftest stabil.", | |||
| "Remote-Bedienung ist gehärtet und auditierbar.", | |||
| "Gerätespezifische Fähigkeiten und Kalibrierungen sind modelliert.", | |||
| "Builds und Deployments sind reproduzierbar." | |||
| ], | |||
| "quality_gates": [ | |||
| "Unit- und Integrationssuite grün", | |||
| "Race Detector grün", | |||
| "Fuzzing ohne kritische Findings", | |||
| "HIL-Regression grün", | |||
| "Soak-Test grün" | |||
| ] | |||
| }, | |||
| "final_instruction_to_ai_team": { | |||
| "summary": "Nicht blind Features ergänzen. Zuerst die Semantik und den Runtime-Kern hart machen. Dann Observability und sichere Steuerung. Danach Hardware-Wahrheit, Kalibrierung und Nightly-Regressions. Alles, was nicht deterministisch, messbar und fault-beherrscht ist, ist noch nicht pro-level.", | |||
| "first_step_now": [ | |||
| "WS-03 beginnen: Parameterinventar und semantische Vereinheitlichung.", | |||
| "Danach direkt WS-01 und WS-02 in einem Architektur-Branch umsetzen." | |||
| ] | |||
| } | |||
| } | |||