README.md

# TeltonikaCodec

[![Hex.pm](https://img.shields.io/hexpm/v/teltonika_codec.svg)](https://hex.pm/packages/teltonika_codec)
[![CI](https://github.com/neilberkman/teltonika_codec/actions/workflows/elixir.yml/badge.svg)](https://github.com/neilberkman/teltonika_codec/actions/workflows/elixir.yml)

Teltonika GPS tracker protocol parser for Elixir. Decodes Codec 8, Codec 8 Extended, and Codec 16 binary protocols used by Teltonika devices (TAT141, FMB920, FMC130, TMT250, etc.).

## Installation

Add `teltonika_codec` to your list of dependencies in `mix.exs`:

```elixir
def deps do
  [
    {:teltonika_codec, "~> 0.1.0"}
  ]
end
```

## Usage

### Parse IMEI handshake

```elixir
{:ok, imei} = TeltonikaCodec.parse_imei(data)
# {:ok, "352093085698206"}
```

### Parse data packet

Auto-detects the codec (8, 8 Extended, or 16) and returns parsed AVL records:

```elixir
{:ok, records} = TeltonikaCodec.parse_packet(packet)

Enum.each(records, fn record ->
  IO.inspect(record.latitude)    # 42.373737
  IO.inspect(record.longitude)   # 42.373737
  IO.inspect(record.speed)       # 0
  IO.inspect(record.timestamp)   # ~U[2024-01-15 14:30:00.000Z]
  IO.inspect(record.io_elements) # %{elements_1b: %{21 => 3}, ...}
end)
```

### TCP buffer management

Use `check_packet/1` to accumulate TCP data until a complete frame arrives:

```elixir
case TeltonikaCodec.check_packet(buffer) do
  {:complete, packet, rest} ->
    {:ok, records} = TeltonikaCodec.parse_packet(packet)
    # Send ACK so the device stops retransmitting
    :gen_tcp.send(socket, TeltonikaCodec.ack(length(records)))
    # Continue with remaining buffer
    handle_data(rest)

  :incomplete ->
    # Wait for more data
    {:noreply, state}
end
```

### IMEI accept/reject

```elixir
:gen_tcp.send(socket, TeltonikaCodec.imei_accept())  # <<0x01>>
:gen_tcp.send(socket, TeltonikaCodec.imei_reject())   # <<0x00>>
```

## Supported Codecs

| Codec | ID   | IO ID Size | IO Count Size | Variable IO | Generation Type |
| ----- | ---- | ---------- | ------------- | ----------- | --------------- |
| 8     | 0x08 | 1 byte     | 1 byte        | No          | No              |
| 8 Ext | 0x8E | 2 bytes    | 2 bytes       | Yes (NX)    | No              |
| 16    | 0x10 | 2 bytes    | 1 byte        | No          | Yes             |

## AVL Record Fields

Each parsed record contains:

| Field         | Type                      | Description            |
| ------------- | ------------------------- | ---------------------- |
| `timestamp`   | `DateTime.t()`            | GPS fix time           |
| `latitude`    | `float`                   | Decimal degrees        |
| `longitude`   | `float`                   | Decimal degrees        |
| `altitude`    | `integer`                 | Meters                 |
| `heading`     | `integer`                 | Degrees (0-360)        |
| `speed`       | `integer`                 | km/h                   |
| `satellites`  | `integer`                 | Visible GPS satellites |
| `priority`    | `:low \| :high \| :panic` | Record priority        |
| `io_elements` | `map`                     | I/O element groups     |

## Protocol Reference

Based on the [Teltonika Codec](https://wiki.teltonika-gps.com/view/Codec) documentation.

## License

Apache-2.0