docs(miotiq): integrate lat/lon/misc into NebuleAir Pro 4G descriptor

- latitude / longitude: 4 bytes each, offset-unsigned encoding
  (raw = round((deg + 90|180) * 1e6), Miotiq equation x/1000000-90|180)
  to sidestep the absence of signed hex2dec. Precision ~11cm.
- misc: 1 byte, enum 0-6 (documented as extensible without breaking
  the byte layout).
- "No fix" authority = device_status.GPS_NO_FIX bit; defensive rule
  for legacy firmwares documented.
- 9 of the 11 reserved bytes consumed; 2 bytes remain reserved.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Your Name
2026-04-24 11:21:07 +02:00
parent 33c4472350
commit 232f1b7097
2 changed files with 24 additions and 21 deletions

View File

@@ -173,6 +173,8 @@ Reconstitution : `f"{version_major}.{version_minor}.{version_patch}"` → ex. `"
| 5 | Mesure en vélo | | 5 | Mesure en vélo |
| 6 | Mesure en transport en commun | | 6 | Mesure en transport en commun |
Enum extensible — de nouvelles valeurs peuvent être ajoutées (7, 8, …) sans casser l'encodage 1 octet. Un consommateur qui rencontre une valeur inconnue doit la traiter comme *contexte non renseigné* (équivalent à `0`), pas rejeter la trame.
## Exemple complet (NebuleAir_Pro) ## Exemple complet (NebuleAir_Pro)
```json ```json

View File

@@ -99,9 +99,23 @@ Descripteur de référence :
2|version_major|hex2dec||| 2|version_major|hex2dec|||
2|version_minor|hex2dec||| 2|version_minor|hex2dec|||
2|version_patch|hex2dec||| 2|version_patch|hex2dec|||
22|reserved|skip||| 8|latitude|hex2dec|degrees|x/1000000-90|
8|longitude|hex2dec|degrees|x/1000000-180|
2|misc|hex2dec|||
4|reserved|skip|||
``` ```
**Encodage `latitude` / `longitude`**`hex2dec` est **non-signé** côté Miotiq. Pour transmettre des coordonnées négatives sans ambiguïté, le firmware encode avec un offset fixe :
- `raw_lat = round((lat_deg + 90) * 1_000_000)` — range attendue `[0, 180_000_000]`, tient dans uint32.
- `raw_lon = round((lon_deg + 180) * 1_000_000)` — range `[0, 360_000_000]`, tient dans uint32.
Miotiq applique l'équation inverse (`x/1000000-90`, `x/1000000-180`) et exporte directement des degrés WGS84 signés dans le JSON. Précision ~11 cm (6 décimales), conforme à [`CONVENTIONS.md`](../CONVENTIONS.md).
**Pas de sentinelle numérique pour « no fix »** : quand le GPS n'a pas de fix, le firmware positionne le bit `GPS_NO_FIX` dans `device_status` (voir [`json-payload.md`](json-payload.md#device_status--bitfield-boîtier-1-octet)). Le backend ignore `latitude`/`longitude` quand ce bit est levé, indépendamment de leur valeur brute.
Les firmwares antérieurs à cette extension envoyaient déjà 83 octets (bloc `reserved` = 11 zéros), qui décodent désormais comme `lat=-90, lon=-180, misc=0`. Sur ces firmwares `device_status = 0xFF` (= champ non supporté — cf. [`json-payload.md`](json-payload.md#error_flags--bitfield-système-1-octet)) : un backend prudent traite donc la combinaison `device_status == 0xFF && (lat, lon) == (-90, -180)` comme « coords non disponibles ».
Layout octet par octet : Layout octet par octet :
| Offset | Taille | Champ | Unité | Scale | Notes | | Offset | Taille | Champ | Unité | Scale | Notes |
@@ -143,11 +157,12 @@ Layout octet par octet :
| 69 | 1 | `version_major` | | | Version firmware `X.y.z` | | 69 | 1 | `version_major` | | | Version firmware `X.y.z` |
| 70 | 1 | `version_minor` | | | `x.Y.z` | | 70 | 1 | `version_minor` | | | `x.Y.z` |
| 71 | 1 | `version_patch` | | | `x.y.Z` | | 71 | 1 | `version_patch` | | | `x.y.Z` |
| 72 | 11 | `reserved` | | | À ignorer (évolution future du descripteur) | | 72 | 4 | `latitude` | degrés | /1e6 90 | WGS84, offset unsigned. Voir encodage ci-dessus. Ignoré si `device_status.GPS_NO_FIX`. |
| 76 | 4 | `longitude` | degrés | /1e6 180 | WGS84, offset unsigned. Idem. |
| 80 | 1 | `misc` | | | Contexte de mesure 06 (voir [`json-payload.md`](json-payload.md#géolocalisation--contexte)) |
| 81 | 2 | `reserved` | | | À ignorer (évolution future du descripteur) |
| **83** | total | | | | | | **83** | total | | | | |
> Les champs `latitude`, `longitude`, `misc` présents dans le JSON final (voir [`json-payload.md`](json-payload.md#géolocalisation--contexte)) **ne sont pas** dans ce descripteur 83B. **À intégrer** : voir la section [À faire](#à-faire) — l'emplacement naturel est le bloc `reserved` de 11 octets (4+4+1 = 9 octets suffisent).
### MobileAir (17 octets — legacy, pré-descripteur) ### MobileAir (17 octets — legacy, pré-descripteur)
> Ce capteur envoie encore un format binaire packé **sans descripteur Miotiq formel**. Migration prévue vers la même approche descripteur que NebuleAir Pro 4G. > Ce capteur envoie encore un format binaire packé **sans descripteur Miotiq formel**. Migration prévue vers la même approche descripteur que NebuleAir Pro 4G.
@@ -266,23 +281,8 @@ Côté PHP (cf. implémentations existantes `udp_miotiq_byte.php` / `udp_miotiq_
## À faire ## À faire
- [ ] **Intégrer `latitude` et `longitude` dans le descripteur NebuleAir Pro 4G** — aujourd'hui absents du descripteur 83B mais attendus dans le JSON final. Emplacement proposé : dans le bloc `reserved` de 11 octets. Encodage suggéré (à valider) — `hex2dec` signé sur 4 octets chacun, équation `x/1000000` pour obtenir des degrés WGS84 avec précision ~10 cm : - [ ] **Côté firmware NebuleAir Pro 4G** : implémenter l'encodage offset de `latitude`/`longitude` (`raw = round((deg + 90|180) * 1_000_000)`) et positionner `device_status.GPS_NO_FIX` quand il n'y a pas de fix.
- [ ] **Valider en test réel** que l'équation Miotiq `x/1000000-90` est acceptée telle quelle dans la colonne equation (soustraction littérale). Fallback si refusée : firmware envoie `raw / 1000` (millidegrés + 90000 pour lat, + 180000 pour lon) et équation devient `x/1000-90`. À tester une fois, valable à vie.
```
8|latitude|hex2dec|degrees|x/1000000|
8|longitude|hex2dec|degrees|x/1000000|
```
> À confirmer : Miotiq supporte-t-il les entiers **signés** sur `hex2dec` ? Si non, décaler les coordonnées (ex. `(x - 2^31) / 1000000`) via la colonne equation, ou passer en représentation non-signée avec un offset.
- [ ] **Trouver un emplacement pour `misc`** (contexte de mesure 06) — 1 octet `hex2dec` sans unité ni équation. Placement proposé : à la suite de `longitude` dans l'ancien bloc `reserved`.
```
2|misc|hex2dec|||
```
- [ ] Une fois les trois champs ajoutés : il reste **2 octets** sur les 11 du `reserved` initial (4+4+1 = 9). Garder une ligne `4|reserved|skip|||` (2 octets) pour évolutions futures, ou les réallouer.
- [ ] Confirmer l'endianness des champs multi-octets (big-endian supposé). - [ ] Confirmer l'endianness des champs multi-octets (big-endian supposé).
- [ ] Confirmer le caractère signé/non-signé de `battery_current` (décharge = négatif ?). - [ ] Confirmer le caractère signé/non-signé de `battery_current` (décharge = négatif ?).
- [ ] Migrer MobileAir du format binaire 17B vers un descripteur Miotiq formel. - [ ] Migrer MobileAir du format binaire 17B vers un descripteur Miotiq formel.
@@ -297,3 +297,4 @@ Côté PHP (cf. implémentations existantes `udp_miotiq_byte.php` / `udp_miotiq_
| 2026-04-23 | v3 | Format descripteur aligné sur doc officielle Miotiq : 6e colonne = export JSON (W/Y/N), ajout base functions `hex2bin` et `userdef`, colonne `equation` (expression en x). | | 2026-04-23 | v3 | Format descripteur aligné sur doc officielle Miotiq : 6e colonne = export JSON (W/Y/N), ajout base functions `hex2bin` et `userdef`, colonne `equation` (expression en x). |
| 2026-04-23 | v4 | Correction : `string` produit du hex (pas ASCII). Correction ISO_39=PM2.5 et ISO_24=PM10 (inversion). Gaz confirmés (NO₂/CO/H₂S/NH₃/O₃). Lien vers JSON canonique AirCarto 2026. | | 2026-04-23 | v4 | Correction : `string` produit du hex (pas ASCII). Correction ISO_39=PM2.5 et ISO_24=PM10 (inversion). Gaz confirmés (NO₂/CO/H₂S/NH₃/O₃). Lien vers JSON canonique AirCarto 2026. |
| 2026-04-23 | v5 | Extension prévue du descripteur NebuleAir Pro 4G avec `latitude`, `longitude`, `misc` dans le bloc `reserved` — proposition d'encodage dans la section À faire. | | 2026-04-23 | v5 | Extension prévue du descripteur NebuleAir Pro 4G avec `latitude`, `longitude`, `misc` dans le bloc `reserved` — proposition d'encodage dans la section À faire. |
| 2026-04-24 | v6 | Intégration effective de `latitude` (4B), `longitude` (4B), `misc` (1B) dans le descripteur NebuleAir Pro 4G. Encodage offset unsigned (`raw = (deg + 90|180) * 1e6`, équation `x/1000000-90|180`) pour contourner l'absence de signed sur `hex2dec` Miotiq. « No fix » géré par le bit `GPS_NO_FIX` de `device_status`. Reste 2B `reserved`. |