Files
docs/wiki/concepts/avl-data-format.md
T
julian 22b1b069df 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.
2026-04-30 13:20:17 +02:00

6.3 KiB
Raw Blame History

title, type, created, updated, sources, tags
title type created updated sources tags
AVL Data Format (Teltonika canonical) concept 2026-04-30 2026-04-30
teltonika-data-sending-protocols
teltonika-ingestion-architecture
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 0360°, 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.