# `MAVLink2.Frame`
[🔗](https://github.com/isaiahdw/mavlink2/blob/v0.1.0/lib/mavlink2/frame.ex#L1)

MAVLink v2 frame encoding and decoding, for a `MAVLink2.Dialect`.

Wire layout:

    STX             0xFD  (v2); 0xFE (v1) is refused on receive
    len             payload length after truncation
    incompat_flags
    compat_flags
    seq
    sysid           1 byte, or 4 (little endian) with SYSID32
    compid
    msgid           24-bit, little endian
    target_sysid    4 bytes, little endian, with TARGET32 only
    payload
    checksum        16-bit LE, over everything after STX plus crc_extra
    signature       13 bytes, with SIGNED only

## Incompat flags

| Bit | | |
| --- | --- | --- |
| `0x01` | SIGNED | a signature follows the checksum; see `MAVLink2.Signing` |
| `0x02` | SYSID32 | `sysid` is 4 bytes. `encode/2` sets it when `sysid` is over 255 |
| `0x04` | TARGET32 | the header carries `target_sysid`, which takes precedence over a payload `target_system`. `encode/2` sets it when `target_sysid` is not `nil` |

A frame with any other bit is refused.

A message to a system id over 255 carries `target_sysid` and 255 in its
payload `target_system`.

## Payload truncation

v2 drops trailing zero bytes from the payload on the wire, keeping at
least one, and the receiver zero-extends it back to the message's length.
A receiver that does not zero-extend misreads every short frame.

## Decoded frames

`raw` is the frame's wire bytes, for forwarding it unchanged (a
re-encoded frame would lose its signature). `known` is `false` only for a
frame decoded with `unknown: :keep` whose id the dialect lacks: its
checksum was not checked and its payload is as received.

# `t`

```elixir
@type t() :: %MAVLink2.Frame{
  compat_flags: 0..255,
  compid: 0..255,
  incompat_flags: 0..255,
  known: boolean(),
  msgid: non_neg_integer(),
  payload: binary(),
  raw: nil | binary(),
  seq: 0..255,
  signature:
    nil
    | {link_id :: 0..255, timestamp :: non_neg_integer(), &lt;&lt;_::48&gt;&gt;, binary()},
  sysid: 0..4_294_967_295,
  target_sysid: nil | 0..4_294_967_295
}
```

# `decode`

```elixir
@spec decode(binary(), module(), keyword()) ::
  {:ok, t(), binary()} | {:incomplete, binary()} | {:error, term(), binary()}
```

Decode the first complete frame in `bin`.

  * `{:ok, frame, rest}`
  * `{:incomplete, bin}`: more bytes needed; keep `bin`
  * `{:error, reason, rest}`: resynchronise from `rest`

Option `unknown:` is `:skip` (the default) or `:keep`, for an id the
dialect lacks.

Errors:

| | Resumes |
| --- | --- |
| `:no_stx` | at the next STX, so a run of junk is one error |
| `:mavlink_v1_unsupported` | after the v1 STX |
| `{:unsupported_incompat_flags, flags}` | after the STX, as soon as the flags byte has arrived: an unknown flag may change the layout |
| `{:unknown_msgid, id}` | with `unknown: :skip`, after the STX as soon as the header has arrived: without the message's `crc_extra` the frame cannot be checked, so its length byte cannot be trusted |
| `:bad_checksum` | after the STX, since the length byte itself may be the corruption |

With `unknown: :keep`, a frame with an unknown id is returned with
`known: false` once all of it has arrived, and decoding resumes after it.
Noise that starts with an STX then costs up to a frame's length of the
bytes after it, as in the C library and pymavlink.

# `drain`

```elixir
@spec drain(binary(), module(), keyword()) :: {[t()], [term()], binary()}
```

Every complete frame in `bin`, as `{frames, errors, rest}`. Errors are
collected, so a noisy line degrades instead of failing. Takes `decode/3`'s
options.

# `encode`

```elixir
@spec encode(t(), module()) :: {:ok, binary()} | {:error, term()}
```

Encode a frame to wire bytes. `raw` and `known` are ignored.

Errors:

| | |
| --- | --- |
| `{:unknown_msgid, id}` | the dialect does not have `id`: a guessed `crc_extra` makes a frame the peer drops without a word |
| `{:out_of_range, field}` | `seq`, `compid` or a flags byte outside 0..255, `sysid` or `target_sysid` outside 32 bits, or `msgid` outside 24 bits |
| `{:unsupported_incompat_flags, flags}` | a bit in `incompat_flags` other than the three above |
| `:payload_too_long` | the payload, after truncation, is longer than the message |

# `iflag_signed`

```elixir
@spec iflag_signed() :: 1
```

# `stx_v1`

```elixir
@spec stx_v1() :: 254
```

# `stx_v2`

```elixir
@spec stx_v2() :: 253
```

---

*Consult [api-reference.md](api-reference.md) for complete listing*
