Le fichier d'observation
Un enregistrement est un fichier JSON, que l'éditeur l'ait écrit ou vous. Voici ce qu'il peut contenir, et ce que veut dire chaque champ.
Un enregistrement est un simple fichier JSON. Rien dedans n'est un blob binaire, un identifiant dans une base de données, ni une référence à ce site — vous pouvez en écrire un à la main, en engendrer depuis vos propres archives, ou en comparer deux dans une relecture de code.
Chaque extrait ci-dessous est en lecture seule, et chacun connaît le format : placez le curseur dans un objet et faites Ctrl+Espace (⌥+I sur Mac) pour lister toutes les clés qui pourraient y figurer, avec ce que le modèle dit de chacune.
L'observation#
| Champ | Sens |
|---|---|
version | Toujours 1 |
id | Quel témoignage c'est, unique parmi tous les enregistrements : le jour, puis qui l'a vu ("1964-04-24-ZamoraLonnie"), ou le lieu pour un témoin anonyme ("1964-04-24-Socorro"). Ce par quoi un dossier le désigne |
time, endTime | { year, month, day, hour, minute, second, raw }, chaque partie facultative — c'est ainsi que le format énonce « 1954 » ou « vers 05:00 ». raw est la date telle qu'écrite en EDTF, et c'est elle qui fait foi : "1948-07-24T02:45~" (approximative), "2025-06?" (incertaine), "1965-07-01%" (les deux), "19XX" (une année masquée), ou "05:00" seul pour une heure dont on a oublié la date. Les nombres sont tenus en accord avec elle pour ce qui calcule (le ciel, l'horloge). C'est un sous-ensemble d'EDTF (niveau 0, ces qualificatifs sur la date entière, années masquées) ; @rr0/time est le modèle EDTF complet de RR0, dans lequel l'outillage d'UFO@home convertit les dates d'un enregistrement |
durationSeconds | Une alternative à endTime, et c'est elle qui l'emporte si les deux sont là |
utcOffsetHours | L'heure LÉGALE de la montre du témoin (+1 pour la France en 1965). Absent, elle est approchée depuis la longitude, qui ne peut connaître ni l'heure légale ni un changement d'heure |
place | [{ lat, lng, name }] — name est le nom qualifié depuis lequel les coordonnées ont été résolues |
witness | { id, title, lastName, firstNames }, tous facultatifs ; à omettre entièrement pour un témoin anonyme. id est une référence à la personne (sur RR0, son répertoire : "ZamoraLonnie") ; les autres champs la décrivent quand personne ne lui en a encore donné |
description | Le récit en prose — une chaîne, ou une par langue (voir plus bas) |
tags | Une liste de chaînes, écrites en anglais : ce sont des termes techniques, et deux enregistrements qui en partagent un doivent s'y égaler. Chaque lecteur les voit dans sa langue lorsqu'une traduction est connue |
Plusieurs témoins : le dossier#
Chaque témoin a son propre enregistrement, et un enregistrement ne dit pas à quel dossier il
appartient : un témoignage se suffit à lui-même. Ce qui les montre ensemble est le
dossier, qui les désigne :
le case.json d'un dossier RR0, qui énonce le titre,
la date et la classification du cas, et liste tout ce qui lui est arrivé en
events. Ses événements de type sighting sont ses témoignages, chacun
pointant vers l'enregistrement d'un témoin :
{
"id": "ChilesWhitted",
"title": "Chiles et Whitted",
"time": "1948-07-24 02:45",
"events": [
{ "type": "event", "eventType": "sighting", "url": "witness-chiles.json" },
{ "type": "event", "eventType": "sighting", "url": "witness-whitted.json" }
]
}
| Champ | Sens |
|---|---|
id | L'identifiant du dossier lui-même. Sur rr0.org c'est le répertoire du dossier, et il peut être omis ; un fichier de cas isolé l'énonce |
title, time | Le nom du cas, et sa date comme RR0 l'écrit ("1948-07-24 02:45", "1954"). Le lecteur nomme un cas qu'il ouvre par son titre |
events | La chronologie du cas. Seuls les sighting sont rejoués, chacun par son url, lue relativement à l'adresse du fichier de cas (si bien que le même cas marche depuis la page de son dossier comme depuis n'importe où) ; les autres (une analyse, un article, un film, un aveu) sont ceux de RR0 |
Donnez-le à <rr0-sighting src> ou au lecteur, et chaque témoin se choisit
dans une liste. Un enregistrement peut être donné directement, sans dossier, mais un dossier à
un seul témoignage marche de la même façon et nomme ce qu'il montre. Essayez avec
case-chiles-whitted.json
(le jouer).
Le dire en plusieurs langues#
Un enregistrement se transmet d'un lecteur à un autre : chaque champ écrit par un auteur peut
donc porter une chaîne par langue au lieu d'une seule — description, le
title d'une forme ou d'un élément de décor, le label et la
note d'un repère.
{
"description": {
"fr": "Tout le témoignage de Lonnie Zamora, d'un seul tenant…",
"en": "Lonnie Zamora's whole testimony, of a piece…"
}
}
Les clés sont des étiquettes de langue telles qu'un navigateur les donne (fr,
en, pt-BR), et aucune n'est obligatoire. Une chaîne simple reste
parfaitement valide et signifie « dans la langue où cela a été écrit » — ce qu'est tout
enregistrement antérieur. Un lecteur dont aucune langue n'est présente reçoit ce que le
fichier A, plutôt qu'un champ vide : une traduction manquante ne doit jamais transformer ce
qu'un témoin a dit en ce qu'il n'a pas dit.
La langue reçue est celle du navigateur, sauf si la page en dit autre chose : un
lang sur l'élément lui-même, ou sur ce qui l'entoure, est pris d'abord — un
article qui déclare sa langue a déjà énoncé dans quelle langue son lecteur le lit. La liste du
navigateur vient ensuite : déclarer une langue force donc un choix sans jeter les autres.
L'éditeur montre une langue, celle du lecteur, et n'écrit que dans celle-là — ouvrir le fichier dans l'autre langue et taper est donc la façon d'ajouter une traduction, et un auteur ne peut pas effacer celle d'un autre.
Ce qui a été vu#
timeline.keyframes est une liste de { t, shapes }, t en
millisecondes depuis le début. Chaque forme porte un sourceId — plusieurs formes
peuvent partager une chronologie (le phénomène, une flamme qui traîne, une seconde lumière) — et
une shape :
{
"kind": "oval",
"bounds": { "x": 0, "y": 0, "width": 0, "height": 0 },
"color": "#39ff14",
"angle": 0,
"transparency": 0,
"haloScale": 1.5,
"brightness": 0,
"blur": 0,
"selected": false,
"title": "le phénomène",
"angular": { "widthDeg": 1.2, "heightDeg": 0.4 }
}
| Champ | Sens |
|---|---|
kind | oval, ou polygon, qui prend alors aussi points |
color | N'importe quelle couleur CSS |
angle | En radians |
transparency | De 0 opaque à 1 invisible |
haloScale | La lueur autour ; 0 pour aucune |
brightness | L'éblouissement : un voile, les aigrettes du diaphragme, un cœur saturé au blanc |
blur | À quel point le témoin a dit les contours indistincts |
angular | Sa taille apparente en degrés — voir plus bas |
C'est angular qui fait foi. bounds est cet angle
projeté sur le canevas fixe de 640×360 au champ de la pose et à travers l'instrument de
l'enregistrement ; il est redérivé au chargement, si bien qu'un fichier survit à un changement
de canevas, de champ ou d'instrument. Si les deux divergent, c'est l'angle qui gagne.
timeline.order est l'ordre de tracé de l'arrière vers l'avant,
timeline.groups les identifiants groupés. Les deux sont facultatifs.
Tout ce qu'il y a autour#
| Champ | Sens |
|---|---|
witnessTrack | { keyframes: [{ t, pose }] } — pose porte lat, lng, elevationM (au-dessus du sol local), headingDeg, pitchDeg, rollDeg, fovDeg, et pour un appareil fNumber et focusDistanceM |
weatherTrack | { keyframes: [{ t, weather }] } — l'état du ciel le long de l'enregistrement : précipitation, vent, orage, et les nuages en couches à hauteur réelle, chacune pouvant porter des nuages individuels placés en mètres. Chaque champ d'un weather est dans la section suivante |
weatherSource | { id, name, url } du relevé d'où vient la météo. Sa présence signifie que l'enregistrement est rejoué tel qu'il a été composé et n'est jamais reconsulté. Absent : le récit du témoin lui-même |
soundTrack | { keyframes: [{ t, sound }] } — kind (none/hum/whistle/rumble/crackle), volume, pitchHz, et un src facultatif vers un vrai enregistrement |
references | Photos des lieux posées sur la scène : src (une adresse, ou une URL data: pour une photo ajoutée depuis un disque), kind (photo/panorama), registration (headingDeg, pitchDeg, rollDeg, fovDeg), opacity, credit/creditUrl, t et drawing facultatifs, et les landmarks sur lesquels elle a été recalée (id, label, picture en { u, v } depuis le coin haut gauche, scene en { azimuthDeg, altitudeDeg }) |
instrument, exposureSeconds | À travers quoi l'observation a été faite, et combien de temps l'obturateur est resté ouvert. Absent : l'œil nu |
decor | Le décor, à une vraie distance eastM/northM du témoin : bâtiments (avec floors, windows), arbres, lampadaires, véhicules, autres témoins, aéronefs — éventuellement avec une track et des lights dont le pattern porte une vraie cadence d'éclats |
Ce que c'était : les interprétations#
Un enregistrement énonce des angles, et un corps en mètres ne fait jamais partie de ce qui
a été vu. C'est une affirmation à son sujet, et elle se met à l'épreuve en la posant dans la
scène et en la regardant depuis l'endroit où se tenait le témoin : elle projette son ombre,
le sol peut la cacher, et son contour est confronté à chaque instant à ce que le témoin a
dit. Une interprétation s'affiche seule, comme le monde qu'elle affirme ; quand on demande
la comparaison (le bouton ◌, ou compare-testimony sur
<rr0-sighting>), le lecteur dessine à côté tout ce que le témoin a vu, en
contours pointillés, et indique l'écart de direction et combien de fois plus large et plus
haut chaque corps paraît, en rouge quand un témoin n'aurait pas pu se tromper d'autant.
La lecture du témoin lui-même va dans l'enregistrement, en interpretation.
Celle d'un analyste va dans le dossier, en événement de type interpretation
qui désigne l'enregistrement par son id, avec qui l'avance dans
by ({ "people": id }, { "org": id }, ou une personne
décrite en valeur) et ses corps sur place ou dans un fichier à url. Un
témoignage dont le témoin a dit ce que c'était se dessine en volume, comme il l'a dit ;
celui qui ne dit rien en mètres se dessine avec les angles qu'il énonce. Le lecteur le
propose, ainsi que chaque interprétation d'analyste, une à la fois.
"interpretation": {
"title": "Un engin posé sur ses pieds",
"bodies": [{
"id": "craft",
"explains": ["ufo-1"],
"model": { "id": "ellipsoid" },
"track": [
{ "t": 52000, "eastM": -571.6, "northM": -965.5, "onGround": true,
"sizeM": { "widthM": 3.36, "lengthM": 3.36, "heightM": 1.73 },
"appearance": { "color": "#e8e6df", "albedo": 0.7 } },
{ "t": 83000, "azimuthDeg": 195.9, "altitudeDeg": 4.1, "distanceM": 44 }
]
}]
}
| Champ | Sens |
|---|---|
explains | Les sourceId des phénomènes que ce corps prétend être |
model | Une forme construite ici (ellipsoid, sphere, disc, cylinder, cone, box, torus), un modèle du catalogue par son id, ou un fichier glTF à url avec son credit. Étiré à sizeM dans tous les cas |
track | Où il est et à quoi il ressemble à chaque t. Une position s'énonce soit dans le monde (eastM/northM depuis l'endroit où se tenait le témoin au début, comme le décor, avec onGround ou altitudeAboveGroundM), soit depuis le témoin à cet instant (azimuthDeg, altitudeDeg, distanceM). Un corps onGround est posé sur le relief ; une direction sans distance rencontre alors le sol là où cette ligne le rencontre. sizeM, attitude (headingDeg, pitchDeg, rollDeg) et appearance (color, albedo) valent jusqu'à ce qu'une keyframe suivante les énonce à nouveau. Une flame (lengthM, widthM, color à la sortie, tipColor, luminanceCdM2) s'allume à la keyframe qui l'énonce, sort du nœud du modèle nommé exhaust (ou de celui que nomme son node), éclaire ce qui l'entoure, soulève de la poussière là où elle touche le sol si raisesDust le dit, et s'éteint avec une luminanceCdM2 de 0 |
outlineNode | Le nœud du modèle qui est ce que le témoin a dessiné ("hull" pour un engin dont les pieds ne figurent pas dans le dessin) : ce sur quoi son contour est mesuré |
smoke | Sur l'interprétation elle-même : ce qu'elle met à brûler au sol, en { eastM, northM, fromT, untilT? }, vu par sa fumée qu'emporte le vent de l'enregistrement |
La météo, et ses nuages#
Un point weather énonce l'état du ciel à un instant de l'horloge de
l'enregistrement ; entre deux points chaque nombre est interpolé, le type de précipitation et
l'orage sont maintenus. Il porte :
| Champ | Sens |
|---|---|
cloudLayers | Les nuages, en liste de couches — voir plus bas. Absent : ce sont les anciens champs des lignes voisines qui les décrivent, adaptés en une couche d'eau et un voile de cirrus ; une liste vide : un ciel dégagé que quelqu'un a regardé |
cloudCover, lowerCloudCover, highCloudCover | Fractions de ciel (0–1) : le total, les couches d'eau seules, le voile glacé seul. Écrits par les enregistrements d'avant les couches, et encore tenus à jour par l'éditeur comme résumé de celles-ci |
cloudBaseM, cloudDarkness | La base unique de la même époque, en mètres au-dessus du sol de référence, et une teinte unique (0 blanc, 1 très sombre) |
iceCrystalAlignment | 0–1, la régularité de la chute des cristaux de glace — ce qui fait d'un anneau nu des parhélies, des arcs et un pilier. Aucun relevé ne le mesure ; une couche de cirrus porte le sien |
relativeHumidity | 0–1, près du sol. Elle décide de la blancheur du ciel clair : la brume gonfle d'eau à mesure que l'air approche de la saturation. Un relevé consulté la porte (tirée de la température et du point de rosée d'ERA5) ; absente, une brume ordinaire |
precipitationType, precipitationIntensity | none/rain/snow/hail, et 0–1 |
windDirectionDeg, windSpeed | Le vent général : le cap VERS lequel il souffle, dans le sens horaire depuis le nord, et des mètres par seconde. C'est lui qui porte les nuages — depuis l'instant zéro, si bien qu'une recherche et une relecture donnent le même ciel |
storm | Éclairs et tonnerre, au bon retard |
Chaque couche de cloudLayers est une nappe de nuages à une
hauteur réelle, et reste elle-même d'un point à l'autre :
| Champ | Sens |
|---|---|
id | Stable d'un point à l'autre — les couches s'apparient par lui, jamais par leur rang dans la liste. Une couche présente à un point et absente au suivant s'estompe ; les réordonner ne change rien |
type | cumulus, stratus, stratocumulus, cirrus ou unknown. Il décide de la forme des sommets et de la finesse du voile ; un cirrus est aussi celui qui réfracte les halos. Il bascule au point, il n'est pas interpolé |
baseM, thicknessM | En mètres. La base est au-dessus du sol de RÉFÉRENCE de l'enregistrement, pas au-dessus d'un témoin qui grimpe ; un témoin plus haut que la base est dans la nappe ou au-dessus, et le ciel est dessiné en conséquence |
coverage | 0–1, et cela veut dire ce que cela dit : la fraction du ciel que cette couche couvre, quelle que soit la taille de ses nuages |
sizeM | La largeur caractéristique d'un nuage, en mètres. Indépendante de la couverture : une même fraction de ciel peut être beaucoup de petits nuages ou quelques gros |
density | 0–2, l'opacité de la matière nuageuse ; 0 est transparent. Indépendante de la couverture elle aussi |
darkness | 0 blanc à 1 très sombre. Absent : le cloudDarkness du point |
seed | Lequel des motifs, parmi les innombrables que les mêmes nombres peuvent dessiner. Absent : un motif dérivé de l'id, ce qui est la raison pour laquelle l'id ne doit pas changer |
windDirectionDeg, windSpeed | Le vent propre à cette couche, lorsqu'il diffère du vent général — c'est le cas de la couche haute d'ordinaire. Absent : le vent général |
iceCrystalAlignment | Pour un cirrus seulement |
instances | Les nuages individuels de cette couche — voir plus bas |
Un nuage individuel dans instances est un nuage de sa couche que
le fichier place exactement, parce que le récit le fait : celui derrière lequel le phénomène
est passé, celui qui était là et nulle part ailleurs. Il est dessiné comme un nuage de sa
couche — même texture, même seuil — et ne se distingue de ses voisins que par sa position et sa
taille ; il est là même quand la coverage de la couche est nulle. Il suit le vent
de la couche comme les autres, et il masque un phénomène devant lequel il passe.
| Champ | Sens |
|---|---|
id | Stable d'un point à l'autre, même règle que pour une couche |
eastM, northM | Où était son centre à l'instant zéro, en mètres depuis le point de départ du témoin. Le vent l'emporte de là |
baseM, thicknessM | Sa propre base et sa propre hauteur, en mètres — un nuage peut être plus bas ou plus haut que sa nappe |
widthM, depthM, rotationDeg | Son emprise, en mètres, et le cap vers lequel cette emprise est tournée |
density, darkness | Les siens ; une obscurité absente est celle de la couche |
{
"weather": {
"cloudLayers": [
{
"id": "low", "type": "cumulus",
"baseM": 1500, "thicknessM": 800,
"coverage": 0.55, "sizeM": 1400, "density": 1, "darkness": 0.15,
"instances": [
{ "id": "the-one", "eastM": 0, "northM": 4200,
"baseM": 1500, "thicknessM": 800,
"widthM": 1900, "depthM": 1300, "rotationDeg": 12, "density": 1 }
]
},
{ "id": "high", "type": "cirrus", "baseM": 8000, "thicknessM": 400,
"coverage": 0.2, "sizeM": 2200, "density": 0.35, "iceCrystalAlignment": 0.65 }
],
"precipitationType": "none", "precipitationIntensity": 0,
"windDirectionDeg": 90, "windSpeed": 5, "storm": false
}
}
Un enregistrement dont la météo a été relevée (il a un
weatherSource) garde la réponse du relevé, pas un lien vers lui : ERA5 donne les
bandes basse, moyenne et haute en trois couches nommées record-low,
record-mid et record-high, la base basse estimée depuis l'écart entre
température et point de rosée, les deux autres à 3 500 m et 8 000 m. Leur type est
unknown (cirrus pour la haute), leur taille et leur densité sont des hypothèses de
dessin : une réanalyse sait quelle part de chaque bande était couverte, pas à quoi les nuages
ressemblaient. Redemandez le relevé depuis l'éditeur et les couches sont réécrites ; modifiez
une couche à la main et l'enregistrement devient celui de l'auteur, la source retirée.
Un fichier entier#
Le plus petit enregistrement qui énonce encore quelque chose — un ovale silencieux traversant le ciel en douze secondes, à une date réelle et en un lieu réel. Tout le reste du format est facultatif, et tout ce qui suit sert à quelque chose :
{
"version": 1,
"description": "The smallest recording that still states something: one silent orange oval, a degree and a half wide, crossing the south-western sky from left to right over twelve seconds.",
"time": { "year": 2024, "month": 8, "day": 12, "hour": 22, "minute": 30 },
"durationSeconds": 12,
"utcOffsetHours": 2,
"place": [{ "lat": 45.19, "lng": 5.72, "name": "Grenoble, Isère, France" }],
"timeline": {
"keyframes": [
{
"t": 0,
"shapes": [
{
"sourceId": "ufo-1",
"shape": {
"kind": "oval",
"bounds": { "x": 150, "y": 150, "width": 10, "height": 3 },
"angular": { "widthDeg": 1.6, "heightDeg": 0.5 },
"color": "#ff8c1a",
"angle": 0,
"transparency": 0,
"haloScale": 1.2,
"selected": false
}
}
]
},
{
"t": 12000,
"shapes": [
{
"sourceId": "ufo-1",
"shape": {
"kind": "oval",
"bounds": { "x": 480, "y": 130, "width": 10, "height": 3 },
"angular": { "widthDeg": 1.6, "heightDeg": 0.5 },
"color": "#ff8c1a",
"angle": 0,
"transparency": 0,
"haloScale": 1.2,
"selected": false
}
}
]
}
]
},
"witnessTrack": {
"keyframes": [
{
"t": 0,
"pose": { "lat": 45.19, "lng": 5.72, "elevationM": 0, "headingDeg": 215, "pitchDeg": 20, "fovDeg": 60 }
}
]
},
"soundTrack": { "keyframes": [{ "t": 0, "sound": { "kind": "none", "volume": 0, "pitchHz": 100 } }] }
}
Pour le modifier et le voir jouer, collez-le dans le lecteur, dont l'éditeur complète sur chaque clé du format, propose les mots que chacune accepte, et dit ce que le modèle en dit.
C'est /demo-data/example-minimal.json
sur ce site : vous pouvez le récupérer, et
le jouer avant d'y toucher.
Remarquez que angular et bounds y figurent tous deux : l'angle est ce
que le fichier SIGNIFIE, et les pixels en sont redérivés au chargement — écrivez l'angle, et
laissez corriger une mauvaise estimation des pixels.
De plus gros, à lire#
Chaque démo de ce site est un simple fichier que vous pouvez ouvrir. Ces quatre-là valent la lecture pour voir comment un vrai enregistrement est bâti :
| Fichier | Ce qu'il faut y regarder |
|---|---|
witness-chiles.json | Un vrai dossier : un témoin, un identifiant de dossier partagé avec un second enregistrement, dix keyframes, un weatherTrack relevé avec son weatherSource |
sky-test-halos.json | Aucun phénomène — un ciel réglé par un weatherTrack dont les images clés font varier l'alignement des cristaux, la couverture de cirrus et une couche de cumulus, vu par un witnessTrack qui balaie le cortège puis s'arrête |
sky-test-clouds.json | Trois couches nuageuses avec altitude, épaisseur, taille, densité et vent en mètres, évoluant sur la timeline météo — et dans la première une entrée instances : un nuage du champ, placé et dimensionné en mètres, qui grossit et s'assombrit sur les deux minutes |
sky-test-aircraft.json | Un instrument et un exposureSeconds, et un décor d'aéronef avec sa track et sept lights à leurs cadences réelles |
instrument-instamatic.json | La même observation que witness-socorro.json, à un champ près. Comparez les deux |
Quatre règles qui décident du sens d'un fichier#
- Les champs discrets sont tenus, les continus sont interpolés. Une forme
absente d'un keyframe ultérieur reste dans son état ; une forme dont le premier keyframe est
à cinq secondes est déjà peinte, dans cet état, dès zéro. Pour qu'une chose cesse d'être
visible, posez-lui un keyframe à
transparency: 1. - Des angles, rien d'autre. Aucune taille ni distance réelle n'est stockée
où que ce soit. Les mètres sont déduits, en inégalités, de ce que le phénomène a été déclaré
passer derrière ou devant (
decor[].occludesSourceIds). - L'énoncé l'emporte sur le déduit.
occludesSourceIdscontient des affirmations du témoin. Rien dans ce format ne peut les déduire : il décrit une apparence dans un champ de vision, pas une position dans l'espace. - Absent n'est pas zéro. Pas de piste sonore signifie que personne n'a posé
la question ;
kind: "none"signifie que le témoin a déclaré n'avoir rien entendu. La même distinction traverse la météo et les nuages de glace.
Le README porte la référence complète, champ par champ, avec le raisonnement derrière chaque choix ; c'est lui qui fait foi si cette page et lui venaient à diverger.