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

ModuleContenuGarantie
:coreFFT, émergence, suivi d'ordres, Kalman, cinématique, session, analyse WAV, codec télémétrieKotlin pur, testé JVM, ≥ 90 % de couverture de ligne (koverVerify en CI)
:appUI Compose, ViewModels, capture micro, GNSS, lecture, stockage, exports PNG/PDFLint 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.
Le mode d'analyse (WAV/vidéo) dispose d'une échappatoire 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

ThreadRôleGarantie
MainUI, écriture des StateFlow, décisions de modeaucun DSP, aucune I/O lourde
nvh-dspUN consommateur de frames : FFT, émergence, suivi d'ordres, enregistrementun seul consommateur (structuralement : flatMapLatest), file bornée à 64 frames DROP_OLDEST
nvh-gnsscallbacks de localisation + diagnostic GNSSjamais le main ; latence de livraison tracée
IO / Defaultchargements WAV/vidéo, STFT plein fichier, filtres, exports PNG/PDFannulables, 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

  1. AudioRepository lit 1024 échantillons (50 % d'overlap), ancre l'horloge sur AudioRecord.getTimestamp(TIMEBASE_BOOTTIME) et émet un CapturedAudioFrame (temps du 1er échantillon et du centre).
  2. CaptureEngine pousse la frame dans la file bornée.
  3. Le consommateur nvh-dsp évalue la vitesse au temps de capture (SpeedProvider.telemetryAt(centerTimeNanos)), fait tourner LiveAnalysisEngine puis OrderTrackingEngine, 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é dans onCleared.
  • Persistence : SettingsStore restaure 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 : AppGraph est process-scoped — l'état de mesure survit à la recréation de l'Activity (choix documenté DEV-25) ; les ViewModels dé-enregistrent leurs hooks dans onCleared.

Mode rapport & exports

  • toggleReportMode fige les historiques courants dans ReportViewModel ; le tracé manuel des ordres est calculé par SmartPathTracker (:core, testé).
  • PngExporter rend la vue figée (spectrogramme + graphes empilés) hors du thread UI, échelle temporelle = données réelles.
  • PdfReportGenerator produit 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.