HERMES Modem
Hermes ARQ/Broadcast modem
Loading...
Searching...
No Matches
arq_protocol.h
Go to the documentation of this file.
1/* HERMES Modem — ARQ Protocol: wire format, mode timing, codec API
2 *
3 * Copyright (C) 2025 Rhizomatica
4 * Author: Rafael Diniz <rafael@riseup.net>
5 *
6 * SPDX-License-Identifier: GPL-3.0-or-later
7 */
8
9#ifndef ARQ_PROTOCOL_H_
10#define ARQ_PROTOCOL_H_
11
12#include <stdbool.h>
13#include <stddef.h>
14#include <stdint.h>
15#include <stdatomic.h>
16
17/* ======================================================================
18 * Protocol version (informational — not carried in wire frames)
19 * ====================================================================== */
20
21#define ARQ_PROTO_VERSION 4 /* v4: framer extension field, no proto_ver field on wire */
22
23/* ======================================================================
24 * Frame header layout (v4, 8 bytes total)
25 *
26 * Proto_ver field removed — both sides always run the same binary.
27 * ack_delay reduced to 1 byte (10ms units, max 2.55s — covers all real delays).
28 * HAS_SNR bit removed — snr_raw==0 already signals "unknown".
29 *
30 * Byte 0: framer byte — set/read by write_frame_header()/parse_frame_header()
31 * bits [7:5] = packet_type (3 bits: PACKET_TYPE_ARQ_CONTROL=0, ARQ_DATA=1, ARQ_CALL=2)
32 * bits [4:0] = extension field (packet-type-specific)
33 * Byte 1: subtype — arq_subtype_t
34 * Byte 2: flags — bit7=TURN_REQ, bit6=HAS_DATA, bits[5:0]=spare
35 * Byte 3: session_id — random byte chosen by caller at connect time
36 * Byte 4: tx_seq — sender's frame sequence number
37 * Byte 5: rx_ack_seq — last sequence number received from peer
38 * Byte 6: snr_raw — local RX SNR feedback to peer; 0=unknown
39 * encoded as uint8_t: (int)round(snr_dB) + 128, clamped 1-255
40 * Byte 7: ack_delay — IRS→ISS: time from data_rx to ack_tx, in 10ms units; 0=unknown
41 * ISS computes: OTA_RTT = (ack_rx_ms - data_tx_start_ms) - ack_delay×10
42 *
43 * CONNECT frames (CALL/ACCEPT) use a separate compact layout — see below.
44 * They are identified by PACKET_TYPE_ARQ_CALL in the framer byte.
45 *
46 * Payload bytes (DATA frames only) follow immediately after byte 7.
47 * ====================================================================== */
48
49#define ARQ_HDR_SUBTYPE_IDX 1
50#define ARQ_HDR_FLAGS_IDX 2
51#define ARQ_HDR_SESSION_IDX 3
52#define ARQ_HDR_SEQ_IDX 4
53#define ARQ_HDR_ACK_IDX 5
54#define ARQ_HDR_SNR_IDX 6
55#define ARQ_HDR_DELAY_IDX 7
56#define ARQ_FRAME_HDR_SIZE 8 /* bytes 0-7 inclusive */
57
58/* CONNECT frames (CALL/ACCEPT) compact layout — 14 bytes, DATAC13 only.
59 * Uses PACKET_TYPE_ARQ_CALL in the framer byte.
60 *
61 * Byte 0: framer byte (PACKET_TYPE_ARQ_CALL | BW token, set by write_frame_header)
62 * Byte 1: connect_meta = (session_id & 0x7F) | (is_accept ? 0x80 : 0x00)
63 * Bytes 2-3: CRC16-CCITT of DST callsign (little-endian) — for local validation
64 * Bytes 4-13: arithmetic_encode(SRC callsign only) — 10 bytes, fits callsigns up to ~14 chars
65 */
66#define ARQ_CONNECT_SESSION_IDX 1
67#define ARQ_CONNECT_PAYLOAD_IDX 2
68#define ARQ_CONNECT_SESSION_MASK 0x7F
69#define ARQ_CONNECT_ACCEPT_FLAG 0x80
70#define ARQ_CONTROL_FRAME_SIZE 14
71#define ARQ_CONNECT_META_SIZE 2 /* framer byte + connect_meta byte */
72#define ARQ_CONNECT_MAX_ENCODED (ARQ_CONTROL_FRAME_SIZE - ARQ_CONNECT_META_SIZE)
73#define ARQ_CONNECT_DST_CRC_SIZE 2 /* CRC16-CCITT of DST at bytes [2..3], little-endian */
74#define ARQ_CONNECT_SRC_MAX_ENCODED (ARQ_CONNECT_MAX_ENCODED - ARQ_CONNECT_DST_CRC_SIZE) /* 10 */
75
76/* Compact CQ frame layout — 14 bytes, DATAC13 only.
77 * Uses PACKET_TYPE_ARQ_CQ in the framer byte.
78 *
79 * Byte 0: framer byte (PACKET_TYPE_ARQ_CQ | BW token)
80 * Bytes 1-13: arithmetic_encode(SRC callsign only)
81 */
82#define ARQ_CQ_PAYLOAD_IDX 1
83#define ARQ_CQ_SRC_MAX_ENCODED (ARQ_CONTROL_FRAME_SIZE - ARQ_CQ_PAYLOAD_IDX) /* 13 */
84
85/* BW token values carried in the framer byte extension field for ARQ_CALL/ARQ_CQ. */
86#define ARQ_BW_TOKEN_NONE 0
87#define ARQ_BW_TOKEN_500 1
88#define ARQ_BW_TOKEN_2300 2
89#define ARQ_BW_TOKEN_2750 3
90
91/* ======================================================================
92 * Flags byte (byte 2)
93 * ====================================================================== */
94
95#define ARQ_FLAG_TURN_REQ 0x80 /* bit 7: sender requests role turn */
96#define ARQ_FLAG_HAS_DATA 0x40 /* bit 6: sender has data queued (IRS→ISS) */
97#define ARQ_FLAG_LEN_HI 0x20 /* bit 5: DATA frames only — payload_valid *
98 * field carries bits [7:0] of valid byte *
99 * count; this flag carries bit 8, allowing *
100 * counts up to 511 (needed for DATAC1 which *
101 * has 502-byte payloads). */
102
103/* ======================================================================
104 * Frame subtypes
105 * ====================================================================== */
107typedef enum
120 /* Subtype 12 (FLOW_HINT) removed in v3 — replaced by HAS_DATA flag */
122
123/* ======================================================================
124 * Parsed frame header (in-memory representation, not wire layout)
125 * ====================================================================== */
127typedef struct
129 uint8_t packet_type; /* PACKET_TYPE_ARQ_CONTROL or _DATA (from framer byte) */
130 uint8_t frame_ext; /* low 5 bits of framer byte */
131 uint8_t subtype; /* arq_subtype_t */
132 uint8_t flags; /* ARQ_FLAG_* bitmask */
133 uint8_t session_id;
134 uint8_t tx_seq;
135 uint8_t rx_ack_seq;
136 uint8_t snr_raw; /* 0=unknown; decode via arq_protocol_decode_snr */
137 uint8_t ack_delay_raw; /* 0=unknown; 10ms units; decode via _decode_ack_delay */
139
140/* ======================================================================
141 * Per-mode timing table
142 *
143 * All times are in seconds (float) measured from the moment PTT goes ON
144 * unless noted otherwise.
145 *
146 * frame_duration_s: empirically measured on-air TX duration.
147 * tx_period_s: expected queue-to-PTT-ON latency (scheduling jitter).
148 * ack_timeout_s: maximum time from PTT-ON until ACK must be received.
149 * ack_timeout ≥ frame_duration + propagation + ACK return
150 * First frame deadline: enqueue_time + tx_period_s + ack_timeout_s
151 * Retry deadline: tx_start_ms + ack_timeout_s
152 * retry_interval_s: = ack_timeout_s + ARQ_ACK_GUARD_S
153 * payload_bytes: usable data bytes per frame.
154 * ====================================================================== */
156typedef struct
158 int freedv_mode; /* FREEDV_MODE_* constant */
159 float frame_duration_s; /* measured TX duration */
160 float tx_period_s; /* queue-to-PTT-ON latency */
161 float ack_timeout_s; /* from PTT-ON to ACK deadline */
162 float retry_interval_s; /* ack_timeout_s + ACK_GUARD_S */
163 int payload_bytes; /* usable payload per frame */
166/* Timing constants shared across modules */
167#define ARQ_CHANNEL_GUARD_MS 700 /* IRS response guard after frame decode.
168 * OFDM decode fires ~200ms before sender
169 * PTT-OFF, so effective gap at sender is
170 * (guard - 200ms) ≈ 500ms. Radio needs
171 * ~340ms for TX→RX switch → 160ms margin
172 * for preamble detection. At 500ms the
173 * effective gap was ~300ms, causing ~50%
174 * ACK loss on DATAC1 (< 340ms switch). */
175#define ARQ_ISS_POST_ACK_GUARD_MS 900 /* ISS guard before resuming DATA TX
176 * after receiving an ACK from the IRS.
177 * Larger than ARQ_CHANNEL_GUARD_MS:
178 * ack_rx fires ~168ms before IRS PTT-OFF,
179 * so the effective gap at IRS is only
180 * (guard + 100ms head) - 168ms.
181 * At 500ms: gap=432ms, too tight for
182 * DATAC1 re-sync after IRS ACK TX.
183 * At 900ms: gap=832ms — 492ms of clear
184 * air before the DATAC1 preamble. */
185#define ARQ_TURN_WAIT_AFTER_ACK_MS 3500 /* IRS post-ACK wait before TURN_REQ:
186 * ISS guard(900ms)+frame(2510ms)+margin */
187#define ARQ_ACCEPT_RX_WINDOW_MS 9000 /* ACCEPTING RX window after ACCEPT TX:
188 * ISS_guard(900)+DATAC4(5800)+margin(2300)
189 * Old value 7000 left only ~300ms margin
190 * and raced with TIMER_RETRY, causing
191 * 3-4 wasted ACCEPT retries (~28s). */
192#define ARQ_ACK_GUARD_S 1 /* extra slack added to retry interval */
193#define ARQ_CALL_RETRY_SLOTS_DEFAULT 4 /* CALL retries before giving up */
194#define ARQ_ACCEPT_RETRY_SLOTS_DEFAULT 4 /* ACCEPT retries before returning */
195#define ARQ_DATA_RETRY_SLOTS_DEFAULT 10 /* DATA retries before disconnect */
196#define ARQ_DISCONNECT_RETRY_SLOTS_DEFAULT 2 /* DISCONNECT frame retries */
198/* Runtime-configurable retry counts (set via RETRIES TCP command).
199 * Macros below preserve existing FSM code unchanged. */
200extern _Atomic int arq_call_retry_slots;
201extern _Atomic int arq_accept_retry_slots;
202extern _Atomic int arq_data_retry_slots;
203extern _Atomic int arq_disconnect_retry_slots;
205#define ARQ_CALL_RETRY_SLOTS atomic_load(&arq_call_retry_slots)
206#define ARQ_ACCEPT_RETRY_SLOTS atomic_load(&arq_accept_retry_slots)
207#define ARQ_DATA_RETRY_SLOTS atomic_load(&arq_data_retry_slots)
208#define ARQ_DISCONNECT_RETRY_SLOTS atomic_load(&arq_disconnect_retry_slots)
209#define ARQ_CONNECT_GRACE_SLOTS 2 /* extra wait slots for ACCEPT */
210#define ARQ_CONNECT_BUSY_EXT_S 2 /* busy-extension guard after CALL */
211#define ARQ_KEEPALIVE_INTERVAL_S 20 /* keepalive TX interval */
212#define ARQ_KEEPALIVE_MISS_LIMIT 5 /* missed keepalives before disconnect */
213#define ARQ_TURN_REQ_RETRIES 2
214#define ARQ_MODE_REQ_RETRIES 2
215#define ARQ_PEER_PAYLOAD_HOLD_S 15 /* hold peer payload mode after activity */
216#define ARQ_IRS_INACTIVITY_CYCLES 5 /* TIMER_PEER_BACKLOG cycles without
217 * RX before IRS keepalive probe */
218#define ARQ_IRS_INACTIVITY_S (ARQ_PEER_PAYLOAD_HOLD_S * \
219 ARQ_IRS_INACTIVITY_CYCLES)
220#define ARQ_MODE_SWITCH_HYST_COUNT 1 /* SNR provides stability gate; 1 = immediate */
221#define ARQ_STARTUP_MAX_S 8 /* DATAC13-only startup window */
222#define ARQ_STARTUP_ACKS_REQUIRED 1
223#define ARQ_SNR_HYST_DB 1.0f
224#define ARQ_SNR_MIN_DATAC4_DB -4.0f /* target MPP SNR (codec2 README) */
225#define ARQ_SNR_MIN_DATAC3_DB -1.0f
226#define ARQ_SNR_MIN_DATAC1_DB 3.0f
227#define ARQ_BACKLOG_MIN_DATAC3 56
228#define ARQ_BACKLOG_MIN_DATAC1 126
229#define ARQ_BACKLOG_MIN_BIDIR_UPGRADE 48 /* > DATAC4 payload capacity */
230#define ARQ_LADDER_LEVELS 3 /* 0=DATAC4, 1=DATAC3, 2=DATAC1 */
231#define ARQ_LADDER_UP_SUCCESSES 4 /* clean ACKs required to step up */
232#define ARQ_RETRY_DOWNGRADE_THRESHOLD 2 /* consecutive retries to force downgrade */
233#define ARQ_MODE_HOLD_AFTER_DOWNGRADE_S 15 /* hold lower mode after forced downgrade */
234
235/* In DATA frames the ack_delay byte is repurposed to carry payload_valid:
236 * 0 = full frame (all user bytes are valid data)
237 * 1 .. user_bytes = only this many leading bytes are valid; rest is padding
238 * This lets partial last-frames be transmitted while still filling the full
239 * modem slot; the receiver uses `data_len` to distinguish valid payload bytes
240 * from trailing padding. */
241#define ARQ_DATA_LEN_FULL 0
242
243/* Mode table (defined in arq_protocol.c) */
244extern const arq_mode_timing_t arq_mode_table[];
245extern const int arq_mode_table_count;
246
247/* ======================================================================
248 * Frame codec API
249 * ====================================================================== */
250
255int arq_protocol_encode_hdr(uint8_t *buf, size_t buf_len, const arq_frame_hdr_t *hdr);
256
261int arq_protocol_decode_hdr(const uint8_t *buf, size_t buf_len, arq_frame_hdr_t *hdr);
262
267uint8_t arq_protocol_bw_token_from_hz(int bw_hz);
268
273int arq_protocol_bw_hz_from_token(uint8_t bw_token);
274
279uint8_t arq_protocol_encode_snr(float snr_db);
280
285float arq_protocol_decode_snr(uint8_t snr_raw);
286
290uint8_t arq_protocol_encode_ack_delay(uint32_t delay_ms);
291
295uint32_t arq_protocol_decode_ack_delay(uint8_t raw);
296
301const arq_mode_timing_t *arq_protocol_mode_timing(int freedv_mode);
302
307uint16_t arq_protocol_callsign_crc16(const char *callsign);
308
309/* ======================================================================
310 * Frame builder API
311 *
312 * Each function fills `buf` (caller-provided) with a complete ready-to-TX
313 * frame (framer byte + header/payload) and returns the total byte count,
314 * or -1 if buf_len < required size or arguments are invalid.
315 *
316 * The framer byte (byte 0, extension field + packet_type) is written by
317 * write_frame_header() inside each builder.
318 *
319 * For control frames, frame_size = ARQ_CONTROL_FRAME_SIZE (14 bytes).
320 * Callers typically allocate INT_BUFFER_SIZE and pass ARQ_CONTROL_FRAME_SIZE.
321 * ====================================================================== */
322
323/* --- Control frames (all use PACKET_TYPE_ARQ_CONTROL) --- */
324
326int arq_protocol_build_ack(uint8_t *buf, size_t buf_len,
327 uint8_t session_id, uint8_t rx_ack_seq,
328 uint8_t flags, uint8_t snr_raw,
329 uint8_t ack_delay_raw);
330
332int arq_protocol_build_disconnect(uint8_t *buf, size_t buf_len,
333 uint8_t session_id, uint8_t snr_raw);
334
336int arq_protocol_build_keepalive(uint8_t *buf, size_t buf_len,
337 uint8_t session_id, uint8_t snr_raw);
338
340int arq_protocol_build_keepalive_ack(uint8_t *buf, size_t buf_len,
341 uint8_t session_id, uint8_t snr_raw);
342
351int arq_protocol_build_turn_req(uint8_t *buf, size_t buf_len,
352 uint8_t session_id, uint8_t rx_ack_seq,
353 uint8_t snr_raw);
354
356int arq_protocol_build_turn_ack(uint8_t *buf, size_t buf_len,
357 uint8_t session_id, uint8_t snr_raw);
358
367int arq_protocol_build_mode_req(uint8_t *buf, size_t buf_len,
368 uint8_t session_id, uint8_t snr_raw,
369 int freedv_mode);
370
372int arq_protocol_build_mode_ack(uint8_t *buf, size_t buf_len,
373 uint8_t session_id, uint8_t snr_raw,
374 int freedv_mode);
375
376/* --- Data frame (PACKET_TYPE_ARQ_DATA) --- */
377
391int arq_protocol_build_data(uint8_t *buf, size_t buf_len,
392 uint8_t session_id, uint8_t tx_seq,
393 uint8_t rx_ack_seq, uint8_t flags,
394 uint8_t snr_raw, uint8_t payload_valid,
395 const uint8_t *payload, size_t payload_len);
396
397/* --- CALL/ACCEPT compact frames (PACKET_TYPE_ARQ_CALL) --- */
398
409int arq_protocol_build_call(uint8_t *buf, size_t buf_len,
410 uint8_t session_id,
411 const char *src, const char *dst,
412 int bw_hz);
413
423int arq_protocol_build_accept(uint8_t *buf, size_t buf_len,
424 uint8_t session_id,
425 const char *src, const char *dst,
426 int bw_hz);
427
438int arq_protocol_parse_call(const uint8_t *buf, size_t buf_len,
439 uint8_t *session_id_out,
440 char *src_out, char *dst_out,
441 int *bw_hz_out);
442
446int arq_protocol_parse_accept(const uint8_t *buf, size_t buf_len,
447 uint8_t *session_id_out,
448 char *src_out, char *dst_out,
449 int *bw_hz_out);
450
454int arq_protocol_build_cq(uint8_t *buf, size_t buf_len,
455 const char *src, int bw_hz);
456
460int arq_protocol_parse_cq(const uint8_t *buf, size_t buf_len,
461 char *src_out, int *bw_hz_out);
462
463#endif /* ARQ_PROTOCOL_H_ */
int arq_protocol_bw_hz_from_token(uint8_t bw_token)
Map an on-air BW token back to Hz.
Definition arq_protocol.c:137
int arq_protocol_build_mode_ack(uint8_t *buf, size_t buf_len, uint8_t session_id, uint8_t snr_raw, int freedv_mode)
MODE_ACK frame — accept peer's mode request.
Definition arq_protocol.c:298
int arq_protocol_build_accept(uint8_t *buf, size_t buf_len, uint8_t session_id, const char *src, const char *dst, int bw_hz)
Build an ACCEPT frame.
Definition arq_protocol.c:491
int arq_protocol_parse_cq(const uint8_t *buf, size_t buf_len, char *src_out, int *bw_hz_out)
Parse a compact DATAC13 CQ frame.
Definition arq_protocol.c:560
_Atomic int arq_disconnect_retry_slots
Definition arq_protocol.c:19
int arq_protocol_build_turn_ack(uint8_t *buf, size_t buf_len, uint8_t session_id, uint8_t snr_raw)
TURN_ACK frame.
Definition arq_protocol.c:278
uint16_t arq_protocol_callsign_crc16(const char *callsign)
Compute CRC16-CCITT of an uppercase-normalised callsign.
Definition arq_protocol.c:337
int arq_protocol_parse_call(const uint8_t *buf, size_t buf_len, uint8_t *session_id_out, char *src_out, char *dst_out, int *bw_hz_out)
Parse a CALL frame; extract callsigns.
Definition arq_protocol.c:518
_Atomic int arq_accept_retry_slots
Definition arq_protocol.c:17
const int arq_mode_table_count
Definition arq_protocol.c:67
_Atomic int arq_call_retry_slots
Definition arq_protocol.c:16
const arq_mode_timing_t * arq_protocol_mode_timing(int freedv_mode)
Look up mode timing entry for a FreeDV mode.
Definition arq_protocol.c:74
int arq_protocol_build_ack(uint8_t *buf, size_t buf_len, uint8_t session_id, uint8_t rx_ack_seq, uint8_t flags, uint8_t snr_raw, uint8_t ack_delay_raw)
ACK frame.
Definition arq_protocol.c:230
int arq_protocol_build_disconnect(uint8_t *buf, size_t buf_len, uint8_t session_id, uint8_t snr_raw)
DISCONNECT frame.
Definition arq_protocol.c:241
int arq_protocol_build_keepalive_ack(uint8_t *buf, size_t buf_len, uint8_t session_id, uint8_t snr_raw)
KEEPALIVE_ACK frame.
Definition arq_protocol.c:259
uint8_t arq_protocol_bw_token_from_hz(int bw_hz)
Map a configured bandwidth in Hz to an on-air BW token.
Definition arq_protocol.c:122
int arq_protocol_build_keepalive(uint8_t *buf, size_t buf_len, uint8_t session_id, uint8_t snr_raw)
KEEPALIVE frame.
Definition arq_protocol.c:250
uint8_t arq_protocol_encode_ack_delay(uint32_t delay_ms)
Encode ack_delay_ms to the 8-bit wire value (10ms units, max 2.55s).
Definition arq_protocol.c:181
int arq_protocol_build_turn_req(uint8_t *buf, size_t buf_len, uint8_t session_id, uint8_t rx_ack_seq, uint8_t snr_raw)
TURN_REQ frame.
Definition arq_protocol.c:268
_Atomic int arq_data_retry_slots
Definition arq_protocol.c:18
int arq_protocol_build_data(uint8_t *buf, size_t buf_len, uint8_t session_id, uint8_t tx_seq, uint8_t rx_ack_seq, uint8_t flags, uint8_t snr_raw, uint8_t payload_valid, const uint8_t *payload, size_t payload_len)
DATA frame — 8-byte header + payload bytes.
Definition arq_protocol.c:309
int arq_protocol_build_call(uint8_t *buf, size_t buf_len, uint8_t session_id, const char *src, const char *dst, int bw_hz)
Build a CALL frame.
Definition arq_protocol.c:483
uint32_t arq_protocol_decode_ack_delay(uint8_t raw)
Decode the 8-bit wire ack_delay to milliseconds.
Definition arq_protocol.c:191
int arq_protocol_build_mode_req(uint8_t *buf, size_t buf_len, uint8_t session_id, uint8_t snr_raw, int freedv_mode)
MODE_REQ frame.
Definition arq_protocol.c:287
uint8_t arq_protocol_encode_snr(float snr_db)
Encode a floating-point SNR (dB) into the snr_raw wire byte.
Definition arq_protocol.c:159
int arq_protocol_build_cq(uint8_t *buf, size_t buf_len, const char *src, int bw_hz)
Build a compact DATAC13 CQ frame carrying source callsign and BW token.
Definition arq_protocol.c:536
const arq_mode_timing_t arq_mode_table[]
Definition arq_protocol.c:59
int arq_protocol_parse_accept(const uint8_t *buf, size_t buf_len, uint8_t *session_id_out, char *src_out, char *dst_out, int *bw_hz_out)
Parse an ACCEPT frame; same layout as CALL.
Definition arq_protocol.c:527
int arq_protocol_decode_hdr(const uint8_t *buf, size_t buf_len, arq_frame_hdr_t *hdr)
Decode the ARQ header from the first bytes of buf.
Definition arq_protocol.c:105
float arq_protocol_decode_snr(uint8_t snr_raw)
Decode snr_raw wire byte back to float dB.
Definition arq_protocol.c:167
arq_subtype_t
Definition arq_protocol.h:104
@ ARQ_SUBTYPE_DISCONNECT
Definition arq_protocol.h:108
@ ARQ_SUBTYPE_ACCEPT
Definition arq_protocol.h:106
@ ARQ_SUBTYPE_DATA
Definition arq_protocol.h:109
@ ARQ_SUBTYPE_CALL
Definition arq_protocol.h:105
@ ARQ_SUBTYPE_KEEPALIVE
Definition arq_protocol.h:110
@ ARQ_SUBTYPE_MODE_ACK
Definition arq_protocol.h:113
@ ARQ_SUBTYPE_TURN_ACK
Definition arq_protocol.h:115
@ ARQ_SUBTYPE_TURN_REQ
Definition arq_protocol.h:114
@ ARQ_SUBTYPE_KEEPALIVE_ACK
Definition arq_protocol.h:111
@ ARQ_SUBTYPE_MODE_REQ
Definition arq_protocol.h:112
@ ARQ_SUBTYPE_ACK
Definition arq_protocol.h:107
int arq_protocol_encode_hdr(uint8_t *buf, size_t buf_len, const arq_frame_hdr_t *hdr)
Encode a parsed header into the first ARQ_FRAME_HDR_SIZE bytes of buf.
Definition arq_protocol.c:89
Definition arq_protocol.h:124
Definition arq_protocol.h:153