Architecture & flux de données
Deux modules Gradle, une session de mesure partagée, trois ViewModels spécialisés et deux threads dédiés. La séparation est imposée par la CI : :core n'a pas le droit d'importer Android.
Modules
| Module | Contenu | Garantie |
|---|---|---|
:core | FFT, émergence, suivi d'ordres, Kalman, cinématique, session, analyse WAV, codec télémétrie | Kotlin pur, testé JVM, ≥ 90 % de couverture de ligne (koverVerify en CI) |
:app | UI Compose, ViewModels, capture micro, GNSS, lecture, stockage, exports PNG/PDF | Lint zéro erreur sans baseline ; texte et couleurs imposés par des gates |
MeasurementSession — l'état de mesure partagé
MeasurementSession (core/…/MeasurementSession.kt) est l'unique détenteur de l'état de mesure : mode de source, historiques spectraux, télémétrie, étiquettes d'ordres, rapport d'émergence, cinématique, réglages d'affichage, provenance et notices. Les trois ViewModels y accèdent en lecture par StateFlow et passent par ses méthodes pour écrire.
Le contrat central est L7 : aucun état d'analyse fantôme ne survit à une transition de source ou de configuration. Concrètement :
- les moteurs DSP (
LiveAnalysisEngine,OrderTrackingEngine) s'enregistrent comme resettables — ils sont effacés synchronement à chaque transition ; - les propriétaires de ressources (micro, GPS, lecteur) s'enregistrent comme hooks de transition de mode — la session ne touche jamais Android elle-même ;
- la provenance (source, route micro, statut de vitesse) est réinitialisée avec le mode, pour qu'aucun rapport ne nomme la mauvaise source.
forceMode() documentée « jamais pour un changement de mode utilisateur » : les chargements de fichiers gèrent eux-mêmes leur effacement sélectif.Threads & concurrence
| Thread | Rôle | Garantie |
|---|---|---|
| Main | UI, écriture des StateFlow, décisions de mode | aucun DSP, aucune I/O lourde |
nvh-dsp | UN consommateur de frames : FFT, émergence, suivi d'ordres, enregistrement | un seul consommateur (structuralement : flatMapLatest), file bornée à 64 frames DROP_OLDEST |
nvh-gnss | callbacks de localisation + diagnostic GNSS | jamais le main ; latence de livraison tracée |
| IO / Default | chargements WAV/vidéo, STFT plein fichier, filtres, exports PNG/PDF | annulables, avec progression annoncée |
CaptureEngine est le seul propriétaire du microphone. Les changements de réglages transitent par flatMapLatest : la capture précédente est annulée (micro libéré via awaitClose) avant que la nouvelle démarre. La classe de bug historique — un consommateur empilé par changement de réglage — y est structurellement impossible. Des compteurs d'intégrité (produced / consumed / restarts) sont journalisés en debug toutes les 256 frames : produced == consumed prouve l'absence de perte.
Flux d'une frame live
AudioRepositorylit 1024 échantillons (50 % d'overlap), ancre l'horloge surAudioRecord.getTimestamp(TIMEBASE_BOOTTIME)et émet unCapturedAudioFrame(temps du 1er échantillon et du centre).CaptureEnginepousse la frame dans la file bornée.- Le consommateur
nvh-dspévalue la vitesse au temps de capture (SpeedProvider.telemetryAt(centerTimeNanos)), fait tournerLiveAnalysisEnginepuisOrderTrackingEngine, et écrit les historiques dans la session.
Cycle de vie des ressources
- Micro & GNSS ne tournent qu'en mode LIVE et avec la permission correspondante (
LiveViewModel.applyResourcePolicy) — revérifié à chaque retour au premier plan. Une permission révoquée en settings est appliquée au resume. - MediaPlayer est possédé par
PlaybackController: préparation suspendue (prepareAsync), sources originale/filtrée explicites,release()idempotent appelé dansonCleared. - Persistence :
SettingsStorerestaure réglages et cinématique au démarrage avant d'armer les observateurs (les défauts ne peuvent pas écraser les valeurs stockées), puis réécrit chaque changement (débounce 500 ms). - Session applicative :
AppGraphest process-scoped — l'état de mesure survit à la recréation de l'Activity (choix documenté DEV-25) ; les ViewModels dé-enregistrent leurs hooks dansonCleared.
Mode rapport & exports
toggleReportModefige les historiques courants dansReportViewModel; le tracé manuel des ordres est calculé parSmartPathTracker(:core, testé).PngExporterrend la vue figée (spectrogramme + graphes empilés) hors du thread UI, échelle temporelle = données réelles.PdfReportGeneratorproduit le rapport client avec le cartouche de traçabilité (ReportStamp) : date, version, source + route micro, paramètres d'analyse, estimateur de vitesse + statut (« causale » / « lissée (RTS) »), confiance k, et la mention non-ECMA de l'indice d'émergence.
Aller plus loin
Chaque classe et fonction de cette architecture est documentée dans la référence API (générée du source). Les méthodes DSP sont détaillées dans Méthodes DSP, la vitesse dans Chaîne GNSS.