Bootstrap LLM-maintained wiki with TRM architecture knowledge
Initialize CLAUDE.md schema, index, and log; ingest three architecture sources (system overview, Teltonika ingestion design, official Teltonika data-sending protocols) into 7 entity pages, 8 concept pages, and 3 source pages with wikilink cross-references.
This commit is contained in:
@@ -0,0 +1,138 @@
|
||||
---
|
||||
title: AVL Data Format (Teltonika canonical)
|
||||
type: concept
|
||||
created: 2026-04-30
|
||||
updated: 2026-04-30
|
||||
sources: [teltonika-data-sending-protocols, teltonika-ingestion-architecture]
|
||||
tags: [teltonika, protocol, parser, data-model]
|
||||
---
|
||||
|
||||
# AVL Data Format
|
||||
|
||||
The canonical Teltonika AVL packet structure across codecs 8, 8E, and 16. This is the byte-level reference the [[teltonika]] adapter implements. See [[teltonika-data-sending-protocols]] for the official source and [[codec-dispatch]] for how the codec ID byte selects between the three formats.
|
||||
|
||||
## Outer envelope (TCP)
|
||||
|
||||
```
|
||||
┌────────────────┬──────────────────┬──────────┬────┬──────────┬────┬───────┐
|
||||
│ Preamble │ Data Field Length│ Codec ID │ N1 │ AVL Data │ N2 │ CRC-16│
|
||||
│ 4B = 0x00000000│ 4B │ 1B │ 1B │ X bytes │ 1B │ 4B │
|
||||
└────────────────┴──────────────────┴──────────┴────┴──────────┴────┴───────┘
|
||||
```
|
||||
|
||||
- **Data Field Length** is computed from the start of `Codec ID` through `N2` (not the whole packet).
|
||||
- **CRC-16/IBM** is computed over the same range. Lower 2 bytes of the 4-byte CRC field carry the value; upper 2 bytes are zero.
|
||||
- **N1 must equal N2** — record count repeated for integrity.
|
||||
- Server ACK is a **4-byte big-endian integer** equal to the number of records accepted. Mismatched count → device retransmits.
|
||||
|
||||
## AVL record (one of N records)
|
||||
|
||||
```
|
||||
[Timestamp 8B][Priority 1B][GPS Element 15B][IO Element X B]
|
||||
```
|
||||
|
||||
- **Timestamp**: UNIX milliseconds since epoch (UTC), big-endian.
|
||||
- **Priority**: enum — `0` Low, `1` High, `2` Panic.
|
||||
|
||||
### GPS Element (15B)
|
||||
|
||||
```
|
||||
[Longitude 4B][Latitude 4B][Altitude 2B][Angle 2B][Satellites 1B][Speed 2B]
|
||||
```
|
||||
|
||||
- **Lat/Lon**: signed 32-bit integer = `((d + m/60 + s/3600 + ms/3600000) × 10⁷)`, where d/m/s/ms are degrees/minutes/seconds/milliseconds. **Two's complement** — first bit = sign (0 positive, 1 negative).
|
||||
- **Altitude**: meters above sea level, signed 16-bit.
|
||||
- **Angle**: heading 0–360°, unsigned 16-bit.
|
||||
- **Satellites**: count of satellites in use, unsigned 8-bit.
|
||||
- **Speed**: km/h, unsigned 16-bit. **`0x0000` when GPS data is invalid** — distinct from "stationary."
|
||||
|
||||
## IO Element — codec-specific
|
||||
|
||||
The IO Element layout is where the three codecs diverge. The parser must dispatch on codec ID to read these correctly.
|
||||
|
||||
### Codec 8 (`0x08`) — 1-byte everything
|
||||
|
||||
```
|
||||
[Event IO ID 1B]
|
||||
[N total 1B]
|
||||
[N1 1B] [IO ID 1B][Value 1B] × N1
|
||||
[N2 1B] [IO ID 1B][Value 2B] × N2
|
||||
[N4 1B] [IO ID 1B][Value 4B] × N4
|
||||
[N8 1B] [IO ID 1B][Value 8B] × N8
|
||||
```
|
||||
|
||||
### Codec 8 Extended (`0x8E`) — 2-byte fields + variable-length
|
||||
|
||||
```
|
||||
[Event IO ID 2B]
|
||||
[N total 2B]
|
||||
[N1 2B] [IO ID 2B][Value 1B] × N1
|
||||
[N2 2B] [IO ID 2B][Value 2B] × N2
|
||||
[N4 2B] [IO ID 2B][Value 4B] × N4
|
||||
[N8 2B] [IO ID 2B][Value 8B] × N8
|
||||
[NX 2B] [IO ID 2B][Length 2B][Value <Length>B] × NX ← variable-length section
|
||||
```
|
||||
|
||||
The **NX section** is unique to 8E. Carries arbitrary-length values (e.g. ICCID-class data, BLE payloads). Each entry self-describes its length. The parser must be length-aware here — getting it wrong silently corrupts subsequent records.
|
||||
|
||||
### Codec 16 (`0x10`) — Generation Type, mixed widths
|
||||
|
||||
```
|
||||
[Event IO ID 2B]
|
||||
[Generation Type 1B] ← unique to Codec 16
|
||||
[N total 1B]
|
||||
[N1 1B] [IO ID 2B][Value 1B] × N1
|
||||
[N2 1B] [IO ID 2B][Value 2B] × N2
|
||||
[N4 1B] [IO ID 2B][Value 4B] × N4
|
||||
[N8 1B] [IO ID 2B][Value 8B] × N8
|
||||
```
|
||||
|
||||
- **No NX section** — Codec 16 does not include variable-size IO elements.
|
||||
- **Generation Type** values: `0`=On Exit, `1`=On Entrance, `2`=On Both, `3`=Reserved, `4`=Hysteresis, `5`=On Change, `6`=Eventual, `7`=Periodical.
|
||||
- Codec 16 is the channel for AVL IDs > 255 on FMB630/FM63XY.
|
||||
|
||||
### Side-by-side
|
||||
|
||||
| | Codec 8 | Codec 8 Extended | Codec 16 |
|
||||
|---|---------|------------------|----------|
|
||||
| Codec ID | 0x08 | 0x8E | 0x10 |
|
||||
| Event IO ID width | 1B | 2B | 2B |
|
||||
| N total / Nk count widths | 1B / 1B | 2B / 2B | 1B / 1B |
|
||||
| IO ID width | 1B | 2B | 2B |
|
||||
| Generation Type | — | — | 1B |
|
||||
| Variable-length IO (NX) | — | yes | — |
|
||||
| AVL IDs > 255 supported | no | yes | yes |
|
||||
|
||||
## Size limits
|
||||
|
||||
- **Minimum AVL record**: 45 bytes (all IO elements disabled).
|
||||
- **Maximum AVL record**: 255 bytes.
|
||||
- **Maximum AVL packet**: 512 bytes for FMB640/FMB641/FMC640/FMM640; **1280 bytes for other devices**.
|
||||
|
||||
The parser should treat oversized packets as malformed (drop the connection per the [[codec-dispatch]] unknown-codec policy — though this is a different malformation, the same loud-failure principle applies).
|
||||
|
||||
## UDP envelope
|
||||
|
||||
When the device runs over UDP instead of TCP:
|
||||
|
||||
```
|
||||
[UDP Channel Header 5B] [AVL Packet Header (1+2+15)B] [AVL Data Array — same as TCP]
|
||||
```
|
||||
|
||||
- **UDP Channel Header**: `Length 2B + Packet ID 2B + Not Usable Byte 1B`.
|
||||
- **AVL Packet Header**: `AVL Packet ID 1B + IMEI Length 2B = 0x000F + IMEI 15B`.
|
||||
- Server ACK over UDP is short: `[UDP Channel Header 5B][AVL Packet ID 1B][Number Accepted 1B]`.
|
||||
|
||||
The AVL Data Array (Codec ID byte + records + N2) is identical to TCP. **The Phase 1 implementation is TCP-only; UDP is documented here for completeness and future evaluation.**
|
||||
|
||||
## Mapping to [[position-record]]
|
||||
|
||||
The parser writes:
|
||||
|
||||
- `device_id` ← IMEI from the handshake (or from the UDP AVL Packet Header).
|
||||
- `timestamp` ← AVL record `Timestamp` (UNIX ms → `Date`).
|
||||
- `latitude`, `longitude`, `altitude`, `angle`, `speed`, `satellites` ← GPS Element fields, with two's-complement decoding for lat/lon and `Speed === 0x0000 ⇒ "GPS invalid"` retained as-is (the Processor decides how to surface it).
|
||||
- `priority` ← AVL record `Priority` (0/1/2).
|
||||
- `attributes` ← every IO element from N1/N2/N4/N8 (and NX for Codec 8E), keyed by **numeric IO ID as string**, value as `number | bigint | Buffer` per IO width. See [[io-element-bag]] for why naming/units stay out of the parser.
|
||||
|
||||
> Note: Codec 16's `Generation Type` and 8E's NX `Length` are not currently in the [[position-record]] shape. Generation Type is codec-defined (not model-defined) and may deserve a typed field — flagged as an open question on the source page.
|
||||
Reference in New Issue
Block a user