AZS

Simulation de dispersion balistique et gazeuse

Documentation technique · v6.3 · © Eric Perret 2026 · ouvrir la simulation
HTML autonome · zéro dépendance Open-Meteo · Esri (sans clé) Web Workers parallèles RK45 Dormand-Prince Pasquill-Gifford A→F auto AEGL-1/10 HCl CSP · Watchdog · Sanity check
Déploiement et validation SSI

L'outil est distribué sous la forme d'un fichier HTML unique et autonome. Il ne requiert aucune installation, aucun serveur, aucun accès administrateur, et ne modifie rien sur le poste. Son exécution est entièrement locale au navigateur — à l'exception de deux appels réseau sortants vers des services publics :

Destination
Objet · Données transmises
api.open-meteo.com
Profil vertical du vent — requête GET HTTPS. Données transmises : latitude/longitude du pas de tir sélectionné, variables météo demandées. Aucune donnée personnelle. Service gratuit, open-source, hébergé en UE.
server.arcgisonline.com
Tuiles cartographiques — requête GET HTTPS. Données transmises : coordonnées de la zone affichée (zoom, x, y de tuile). Aucune donnée personnelle. Imagerie Esri World Imagery et étiquettes Esri, sans clé ni API, couverture mondiale.

Le fichier HTML contient l'intégralité du code source en clair, lisible sans outil particulier dans n'importe quel éditeur de texte. Il est recommandé, avant tout déploiement dans un environnement sensible, de soumettre ce fichier à une revue de code par l'IA de son choix (Claude, GPT-4, Gemini, etc.) avec la question explicite : "Analyse ce code sous l'angle de la sécurité informatique, des flux réseau, et de la qualité des modèles physiques implémentés. Identifie tout ce qui te semble problématique."

Cette démarche présente plusieurs avantages dans un contexte SSI :

Transparence totale
Le code est en clair, non obfusqué, non minifié. Tout algorithme, toute constante, tout flux réseau est lisible et vérifiable ligne par ligne.
Zéro persistance
Aucune écriture disque, aucun cookie, aucun localStorage. Toutes les données de calcul résident en mémoire RAM le temps de la session navigateur.
Zéro dépendance tierce
Aucune bibliothèque JavaScript externe. Aucun CDN. Aucun framework. La surface d'attaque par la chaîne d'approvisionnement logicielle est nulle.
Fonctionnement hors-ligne
En l'absence de réseau, le fichier s'ouvre et affiche les résultats d'un calcul précédent si les tuiles sont en cache navigateur. La météo seule nécessite une connexion.
Limite de responsabilité. Cet outil est une aide à la décision opérationnelle. Il exploite le profil vent de l'heure courante (analyse météo temps réel open-meteo), la pression QNH et l'humidité relative sol mesurées au moment du calcul. Les modèles physiques implémentés — atmosphère ISA 7 couches, intégrateur RK45 adaptatif, force de Coriolis, Cd(Mach), dispersion Pasquill-Gifford classe C — sont des références scientifiques publiées et documentées. Il ne se substitue pas aux systèmes de sécurité officiels du Centre Spatial Guyanais ni aux procédures réglementaires en vigueur. Les résultats doivent être interprétés par du personnel qualifié.
Interface — capture d'écran v6.3
Capture d'écran simulation dispersion v6.3

Architecture fonctionnelle

Trois domaines indépendants. Le thread principal orchestre les Workers et gère le rendu. Les Workers sont autonomes : ISA, vent, intégrateur — aucun accès DOM.

Affiche
render() — boucle rendu canvas
renderEnveloppes() — polygones balistiques
renderBarbules() — colonne WMO
renderPuffIsoline() — zone HCl + croix
barbule() — dessin WMO
getTile() — cache tuiles Esri (imagerie + étiquettes)
geoToPix() / pixToGeo() — Mercator
metToGeo() — ENU → géo sphère
couleurAlt() — palette altitude
Balistique
simuler() — orchestration couches
runLayerParallel() — pool Workers
Worker : trajCouche() — intégration RK45
Worker : rk45Step() — Dormand-Prince
Worker : fP() — dérivée état ENU
Worker : isaAtmo() — ρ(z), a(z)
Worker : ventA() — interp. dichotomique
convexHullIdx() — Andrew MC O(n log n)
ariane() · deriveAriane() · tempsAriane()
Diffusion
puffPG(z0, profil, classe) — bouffée PG A→F
whatIsGifford(meteo) — classe PG depuis météo réelle
ventMain() — interp. vent principal
getMeteo() — open-meteo API + cache + sanity check
simulerAvecWatchdog() — timeout 3 min
cadrerInitial() — zoom auto avant calcul
conservé non actif :
concPolluants() — plume continue
renderConc() — cartouche sol
Flux de données principal : simuler() → runLayerParallel() → Workers → coque convexe → render(). Calcul gaz : puffPG() → renderPuffIsoline(), indépendant de la balistique.

Domaine balistique

simuler(z0, statusCb) orchestration
Entrée
z0 altitude explosion (m) · statusCb callback progression UI
Traitement
Récupère météo. Initialise 180 points sur cercle 1 km autour du point d'explosion avec vitesse Ariane(z0). Pour chaque niveau météo (ordre décroissant) : 180 pts × 3 grains × 3 facteurs vent = jusqu'à 1620 tâches distribuées aux Workers. Coque convexe entre couches. Rend après chaque couche.
Sortie
Objet res : enveloppes par couche, profil, position explosion, aire sol (m²). Temps calcul, t_first, t_last impacts.
runLayerParallel(tasks, profil, siteLat) Workers
Entrée
Tableau {sIn, zTarget, Cd0, cdType, m, ventF} · profil météo complet · latitude site (rad)
Traitement
Découpe en min(hardwareConcurrency−1, 8) lots équilibrés. postMessage par Worker. Promise.all().
Sortie
Tableau d'états [x, y, z, vx, vy, vz, t] dans l'ordre original des tâches.
Worker : trajCouche(sIn, zTarget, ...) intégration RK45
Entrée
État initial [x, y, z, vx, vy, vz, t] · altitude cible · paramètres grain et vent
Traitement
Boucle RK45 adaptatif. Critère Hairer-Wanner : ATOL=[50m, 50m, 50m, 1m/s, 1m/s, 1m/s, 1s], RTOL=1e-3. DT_MAX=5s. Pas borné par (zLeft/|vz|) pour ne pas dépasser la cible. Interpolation linéaire à l'atterrissage exact sur zTarget. Sécurité 100 000 itérations.
Sortie
État [x, y, z, vx, vy, vz, t] exactement à zTarget.
Worker : fP(s, profil, Cd0, cdType, m, ventF, siteLat) dérivée d'état ENU
Entrée
État courant s · profil vent · paramètres grain et perturbation vent
Traitement
ISA → ρ(z), a(z). Vent interpolé × ventF. v_rel = v_vent − v_grain (3D). Ma = |v_rel|/a. Cd(Ma) table selon type. k = ½·ρ·Cd·A/m. Coriolis : Ω décomposé en ENU (Ω_N=Ω·cos(φ), Ω_U=Ω·sin(φ)). Gravité locale g(z) = g₀·(Rₑ/(Rₑ+z))².
Sortie
[vx, vy, vz, ax, ay, az, 1] — le composant 1 cumule le temps sans coût.
Worker : isaAtmo(z) atmosphère ISA 7 couches
Entrée
z altitude (m), borné à 0 si négatif
Traitement
Recherche couche (7 couches OACI 0–86 km). T(z) = T_base + dT·(z−z_base). Pression : isotherme p=p_b·exp(−g·dz/R·T) ou gradient p=p_b·(T/T_b)^(−g/R·dT). Correction humidité : T_v = T·(1+0.378·e/p), HR(z) = HR_SOL·exp(−z/2000).
Sortie
{rho, a} — densité kg/m³ (air humide) · vitesse son m/s (air sec, convention standard).
convexHullIdx(pts) géométrie — Andrew Monotone Chain
Entrée
Tableau [[x,y],...]
Traitement
Tri lexicographique O(n log n). Balayage bas (gauche→droite) puis haut (droite→gauche). Élimination concavités par produit vectoriel cross(O,A,B)≤0. Dépile si nécessaire.
Sortie
Indices des sommets de la coque convexe dans l'ordre. Réduit les N_PTS × 9 combos aux seuls extrêmes pour la couche suivante — complexité bornée.
ariane(h) · deriveAriane(h) · tempsAriane(h) profil vol Ariane 6
Entrée
h altitude (m)
Traitement
ariane : interpolation linéaire table 11 points (0–50 km). deriveAriane : intégrale trapèzes de tan(pitch)·dh → dérive horizontale dans la direction AZIMUT. tempsAriane : intégrale trapèzes de dh/vz → temps de vol depuis le sol.
Sortie
{v, pitch} · dérive (m) · temps de vol (s) — utilisés pour position initiale des débris et fraction propergol consommée.

Domaine diffusion gazeuse

puffPG(z0, profil, classe) bouffée Pasquill-Gifford A→F
Entrée
z0 altitude explosion (m) · profil vent open-meteo
Traitement
Masse : Q = N_boosters × 142t × (1−t_vol/135s) × 18.8% × (1−0.25). Vent moyen u_moy sur couches 0→z0. σ_y(x)=sy_a·x^0.886, σ_z(x)=sz_a·x^sz_b — coefficients issus de PG_CLASSES[classe]. 6 classes A→F disponibles, coefficients Turner (1970). C_sol(t) = 2Q/((2π)^1.5·σ_y²·σ_z)·exp(−z0²/2σ_z²). Seuil opérationnel = AEGL-1/10 = 0.1 mg/m³. Pic par échantillonnage log 200 pts. t_touch et t_end par dichotomie 80 itérations. z0_seuil par dichotomie si noBreach.
Sortie
Objet puff avec fonctions _Cmax(t) concentration sol · _Ccentre(t) concentration centre nuage (sans atténuation H) · _sy(t) σ_y. Timestamps t_touch, t_peak, t_end. r_aegl1 au pic. Si noBreach : z0_seuil.
whatIsGifford(meteo) classe PG depuis météo réelle
Entrée
Objet météo : u10 vent surface (m/s) · rad_sw radiation solaire (W/m²) · nuage nébulosité (%)
Traitement
Table Pasquill (1961) / Turner (1970). Nuit si rad_sw < 10 W/m². Ciel couvert si nuage > 80% → classe D. Sinon croisement vent surface × insolation (forte >600 · modérée 300-600 · faible <300 W/m²). Retourne A, B, C, D, E ou F.
Sortie
Lettre de classe PG. Utilisée si le select UI est sur AUTO. Sinon ignorée — la classe choisie manuellement prime.
ventMain(z, profil) interpolation vent thread principal
Entrée
z altitude (m) · profil niveaux météo triés par altitude
Traitement
Recherche linéaire du segment encadrant z. Interpolation linéaire wx, wy. Extrapolation constante hors bornes.
Sortie
{wx, wy} composantes vent ENU (m/s).
simulerAvecWatchdog(z0, statusCb) watchdog timeout 3 min
Entrée
Mêmes paramètres que simuler()
Traitement
Promise.race entre simuler() et un timeout de 3 minutes. Si le calcul dépasse 3 min (altitude très élevée, machine lente), rejette proprement la promesse et réactive le bouton. N'arrête pas les Workers — ils finissent leur lot courant.
Sortie
Même retour que simuler(), ou erreur explicite "Calcul interrompu après 3 min".
getMeteo(forceRefresh) open-meteo API + cache
Entrée
forceRefresh booléen · coordonnées SITE globales
Traitement
Court-circuité si PROFIL_VENT_STATIQUE≠null (mode test). Sinon : fetch JSON open-meteo, 16 niveaux hPa + surface_pressure, relative_humidity_2m, wind_speed_10m, shortwave_radiation, cloud_cover. Tranche horaire la plus proche. Conversion direction → ENU. Tri croissant par altitude. Sanity check : vent > 150 km/h → erreur explicite. Cache par nom de site.
Sortie
{profil, heure, ptMesure, qnh, hr, u10, rad_sw, nuage} — profil de 1 à 16 niveaux + données surface temps réel.
CSP (Content Security Policy) : balise meta dans le <head>. Autorise uniquement blob: pour les Workers, api.open-meteo.com pour les connexions sortantes et server.arcgisonline.com pour les images de tuiles. Tout le reste bloqué par défaut.
Sanity check météo : vent > 150 km/h dans n'importe quelle couche → erreur explicite, calcul bloqué avant de démarrer.
concPolluants() — conservée, non active. Modèle plume gaussienne stationnaire (source élevée continue). Adapté aux rejets industriels permanents (cheminées). Inadapté aux rejets accidentels instantanés → remplacé par puffPG().

Domaine affichage

render() boucle rendu canvas
Entrée
État global : carte.lat/lon/zoom · carte.resultat · carte.puff
Traitement
Fond. Imagerie Esri World Imagery + étiquettes Esri Boundaries and Places (cache Map borné à 600 tuiles), léger assombrissement. Repère pas de tir. Si resultat : renderEnveloppes() + renderBarbules(). Toujours : renderPuffIsoline().
Sortie
Canvas 2D complet. Appelée après chaque couche balistique, pan/zoom, resize, et chaque calcul.
renderEnveloppes() polygones balistiques par couche
Entrée
carte.resultat.enveloppes[] — polygones ENU par niveau météo
Traitement
Pour chaque enveloppe : polyEnPix() (metToGeo + geoToPix). Remplissage couleurAlt(z) semi-transparent. Contour plein 2.4px (sol) ou pointillé 1px (couches). Label altitude au sommet (reduce min y). Sol : label "IMPACT SOL" au centroïde. Ligne trajectoire site→explosion. Marqueur explosion vert.
Sortie
Enveloppes colorées du haut (bleu) vers le sol (rouge). Zone sol pleine avec label.
renderBarbules() profil vent WMO colonne
Entrée
carte.resultat.profil[] — niveaux vent filtrés ≤ zMax+500m
Traitement
Repère + croix au point de mesure open-meteo. Colonne verticale 38px/barbule ancrée en dessous. Chaque barbule : convention WMO (fanion △ = 50kt, barbe longue = 10kt, demi = 5kt, cercle = calme). Label altitude (km) + vitesse (m/s) à droite. Couleur = couleurAlt(z).
Sortie
Colonne lisible, colorée par altitude, lue de haut en bas = bas de l'atmosphère vers le haut.
renderPuffIsoline() zone HCl AEGL-1/10
Entrée
carte.puff — objet retourné par puffPG()
Traitement
Si !noBreach : 40 instants entre t_touch et t_end. Pour chaque t : centre dérive (wx_moy·t, wy_moy·t), rayon r=σ_y·√(−2·ln(seuil/C_sol(t))). 24 points par cercle. Coque convexe → haricot rouge pointillé. Croix toutes les 15min de t=0 à t_end : position centre, label "+HhMM · C_centre(t)". Rouge si C_sol(t)>seuil, gris sinon.
Sortie
Haricot rouge = zone exposée > AEGL-1/10. Croix = dérive du centre du nuage avec concentration maximale dans le nuage. Absent si noBreach.
metToGeo(dxE, dyN) projection sphérique directe
Entrée
Déplacement ENU depuis SITE : dxE Est (m) · dyN Nord (m)
Traitement
d = √(dxE²+dyN²). Azimut = atan2(dxE, dyN). Distance angulaire ang = d/R (R=6371km). Projection directe Vincenty simplifié sphère : lat1 = asin(sin(lat0)·cos(ang)+cos(lat0)·sin(ang)·cos(brg)), lon1 = lon0 + atan2(...).
Sortie
[lat, lon] degrés. Erreur <0.1% à 200 km (vs approximation plate qui accumule >1% à 100 km).
barbule(ctx, x0, y0, dirVient, vitesseMS, couleur) dessin WMO canvas
Entrée
Contexte 2D · position (px) · direction d'où vient le vent (rad) · vitesse (m/s) · couleur CSS
Traitement
Conversion m/s → kt, arrondi 5kt. Hampe L=28px dans la direction d'origine. Fanions triangulaires 50kt (remplis), barbes longues 10kt, demi-barbes 5kt. Espacement 6px. Cercle vide si <5kt.
Sortie
Barbule météorologique standard WMO No.306 dessinée sur canvas.

Paramètres éditables

ConstanteValeurRôle · éditer pour
BALLES3 grainsType · Cd₀ · masse (g) · modèle Cd : sphere ou blunt
VENT_PERTURB0.15±15% amplitude vent — ajuster la largeur de l'enveloppe pessimiste
N_PTS180Points cercle initial — réduire pour accélérer, augmenter pour précision
HR_SOL0.80Fallback humidité sol si API indisponible — valeur nominale Kourou. En opérationnel, la valeur live relative_humidity_2m open-meteo est utilisée.
SOL100 mAltitude d'arrêt trajectoires balistiques
RAYON_DISP1000 mRayon cercle de dispersion initial autour du point d'explosion
F_LOFT0.25Fraction HCl aspirée par la boule de feu — réduire pour cas pessimiste
P120C.HCl_AEGL11.0 mg/m³Référence AEGL-1 HCl — seuil opérationnel = /10 dans puffPG()
PG_CLASSES6 classes A→FTable coefficients Pasquill-Gifford — sy_a, sz_a, sz_b par classe
pgClasse (select UI)AUTOAUTO = whatIsGifford() au clic · A…F = classe forcée manuelle
PROFIL_VENT_STATIQUEnullnull = API. Tableau [{z,wx,wy,v,dir,hPa}] pour tests reproductibles
PAS_TIRS18 sitesAjouter un site : {nom, lat, lon, azimut, lanceur, groupe}
P120C_BOOSTERSELA-4:4 ELV:1Nombre de boosters P120C par clé de pad
ARIANE11 pointsProfil de vol — adapter à un autre lanceur

Références scientifiques

DomaineRéférence
ISAOACI doc 7488 — Manual of the ICAO Standard Atmosphere
RK45Dormand & Prince — J. Comput. Appl. Math. 6:19-26, 1980
Cd sphèreAchenbach 1978 · données NACA/NASA sphères
Dispersion PGPasquill (1961) · Gifford (1976) — tables empiriques classes A→F
Bouffée gaussienneSeinfeld & Pandis — Atmospheric Chemistry and Physics (2e éd.)
P120C compositionESA / ArianeGroup — données publiques HTPB/AP/Al
AEGL-1 HClUS EPA — Acute Exposure Guideline Levels · HCl 1h = 1.0 mg/m³
Coriolis ENUFormulation standard — Ω = 7.2921×10⁻⁵ rad/s
Barbules WMOWMO No. 306 — Manual on Codes · wind barb convention
Vincenty sphèreProblème direct géodésique — approximation sphérique R=6371km