Mercury Modem
Mercury 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 * Plain session frame header (8 bytes): the DISCONNECT's. A session's other
19 * frames are the carousel's (carousel.h), CALL/ACCEPT have their own layout
20 * (below).
21 *
22 * Byte 0: framer byte — set/read by write_frame_header()/parse_frame_header()
23 * bits [7:5] = packet_type (PACKET_TYPE_ARQ_CONTROL)
24 * bits [4:0] = extension field (0)
25 * Byte 1: subtype — arq_subtype_t
26 * Byte 2: flags — 0
27 * Byte 3: session_id — the session's id, chosen by the caller
28 * Byte 4: tx_seq — 0
29 * Byte 5: rx_ack_seq — 0
30 * Byte 6: snr_raw — local RX SNR; 0=unknown, else round(dB) + 128
31 * Byte 7: ack_delay — 0
32 * ====================================================================== */
33
34#define ARQ_HDR_SUBTYPE_IDX 1
35#define ARQ_HDR_FLAGS_IDX 2
36#define ARQ_HDR_SESSION_IDX 3
37#define ARQ_HDR_SEQ_IDX 4
38#define ARQ_HDR_ACK_IDX 5
39#define ARQ_HDR_SNR_IDX 6
40#define ARQ_HDR_DELAY_IDX 7
41#define ARQ_FRAME_HDR_SIZE 8 /* bytes 0-7 inclusive */
42
43/* CONNECT frames (CALL/ACCEPT) compact layout — 14 bytes, DATAC16 only.
44 * Uses PACKET_TYPE_ARQ_CALL in the framer byte.
45 *
46 * Byte 0: framer byte (PACKET_TYPE_ARQ_CALL | BW token, set by write_frame_header)
47 * Byte 1: connect_meta = (session_id & 0x7F) | (is_accept ? 0x80 : 0x00)
48 * Bytes 2-3: CRC16-CCITT of DST callsign (little-endian) — for local validation
49 * Bytes 4-13: arithmetic_encode(SRC callsign only) — 10 bytes, fits callsigns up to ~14 chars
50 */
51#define ARQ_CONNECT_SESSION_IDX 1
52#define ARQ_CONNECT_PAYLOAD_IDX 2
53#define ARQ_CONNECT_SESSION_MASK 0x7F
54#define ARQ_CONNECT_ACCEPT_FLAG 0x80
55#define ARQ_CONTROL_FRAME_SIZE 14
56#define ARQ_CONNECT_META_SIZE 2 /* framer byte + connect_meta byte */
57#define ARQ_CONNECT_MAX_ENCODED (ARQ_CONTROL_FRAME_SIZE - ARQ_CONNECT_META_SIZE)
58#define ARQ_CONNECT_DST_CRC_SIZE 2 /* CRC16-CCITT of DST at bytes [2..3], little-endian */
59#define ARQ_CONNECT_SRC_MAX_ENCODED (ARQ_CONNECT_MAX_ENCODED - ARQ_CONNECT_DST_CRC_SIZE) /* 10 */
60
61/* Compact CQ frame layout — 14 bytes, DATAC16 only.
62 * Uses PACKET_TYPE_ARQ_CQ in the framer byte.
63 *
64 * Byte 0: framer byte (PACKET_TYPE_ARQ_CQ | BW token)
65 * Bytes 1-13: arithmetic_encode(SRC callsign only)
66 */
67#define ARQ_CQ_PAYLOAD_IDX 1
68#define ARQ_CQ_SRC_MAX_ENCODED (ARQ_CONTROL_FRAME_SIZE - ARQ_CQ_PAYLOAD_IDX) /* 13 */
69
70/* BW token values carried in the framer byte extension field for ARQ_CALL/ARQ_CQ. */
71#define ARQ_BW_TOKEN_NONE 0
72#define ARQ_BW_TOKEN_500 1
73#define ARQ_BW_TOKEN_2300 2
74#define ARQ_BW_TOKEN_2750 3
75
76/* ======================================================================
77 * Frame subtypes. 1.9.x sent others (DATA, ACK, KEEPALIVE, MODE and TURN
78 * requests); this version reads only the DISCONNECT.
79 * ====================================================================== */
80
81typedef enum
82{
85
86/* ======================================================================
87 * Parsed frame header (in-memory representation, not wire layout)
88 * ====================================================================== */
89
90typedef struct
91{
92 uint8_t packet_type; /* PACKET_TYPE_ARQ_CONTROL (from framer byte) */
93 uint8_t frame_ext; /* low 5 bits of framer byte */
94 uint8_t subtype; /* arq_subtype_t */
95 uint8_t flags;
96 uint8_t session_id;
97 uint8_t tx_seq;
98 uint8_t rx_ack_seq;
99 uint8_t snr_raw; /* 0=unknown; decode via arq_protocol_decode_snr */
102
103/* ======================================================================
104 * Per-mode timing table (arq_protocol.c)
105 * ====================================================================== */
106
107/* Upper bound on frames per modem TX action. */
108#define ARQ_BURST_MAX 5
109
110typedef struct
111{
112 int freedv_mode; /* FREEDV_MODE_* constant */
113 float frame_duration_s; /* measured TX duration */
114 float retry_interval_s; /* wait before repeating a CALL/ACCEPT/DISCONNECT */
115 int payload_bytes; /* frame payload */
117
118/* The one FreeDV mode used for the ARQ control frames (CALL/ACCEPT/
119 * DISCONNECT/CQ, and the carousel's control frames). Swapping the control mode is a
120 * one-line change here; everything else references this define. The mode
121 * must carry exactly ARQ_CONTROL_FRAME_SIZE payload bytes and must be the
122 * only mode with that frame size in arq_mode_table. */
123#define ARQ_CONTROL_MODE FREEDV_MODE_DATAC16
124
125/* Timing constants shared across modules. The two guards are measured, not
126 * tuned, and are no longer configuration: they bundle the radio's TX->RX
127 * switch with the audio backend's buffering, which nobody can tune blind. */
128#define ARQ_CHANNEL_GUARD_MS 700 /* IRS response guard after frame decode.
129 * OFDM decode fires ~200ms before sender
130 * PTT-OFF, so effective gap at sender is
131 * (guard - 200ms) ~= 500ms. Radio needs
132 * ~340ms for TX->RX switch -> 160ms margin
133 * for preamble detection. At 500ms the
134 * effective gap was ~300ms, causing ~50%
135 * ACK loss on DATAC1 (< 340ms switch). */
136
137#define ARQ_ISS_POST_ACK_GUARD_MS 900 /* ISS guard before resuming DATA TX
138 * after receiving an ACK from the IRS.
139 * Larger than ARQ_CHANNEL_GUARD_MS:
140 * ack_rx fires ~168ms before IRS PTT-OFF,
141 * so the effective gap at IRS is only
142 * (guard + 100ms head) - 168ms.
143 * At 500ms: gap=432ms, too tight for
144 * DATAC1 re-sync after IRS ACK TX.
145 * At 900ms: gap=832ms -- 492ms of clear
146 * air before the DATAC1 preamble. */
147#define ARQ_ACCEPT_RX_WINDOW_MS 9000 /* ACCEPTING RX window after ACCEPT TX:
148 * ISS_guard(900)+DATAC15(4400)+margin(3700)
149 * Old value 7000 left only ~300ms margin
150 * and raced with TIMER_RETRY, causing
151 * 3-4 wasted ACCEPT retries (~28s). */
152/* Grace period after entering CALLING/ACCEPTING (i.e. after PENDING is sent)
153 * during which LISTEN OFF is deferred. Scanning hosts (BPQ32) need a moment to
154 * process PENDING and cancel their own dwell timers; without this grace, a
155 * LISTEN OFF that crosses PENDING on the wire cancels the handshake — the host
156 * moves to the next channel and the reply goes out on the wrong frequency.
157 * 2 s is conservative: ~0.5 s of TCP+host processing with headroom. */
158#define ARQ_LISTEN_OFF_GRACE_MS 2000
159#define ARQ_CALL_RETRY_SLOTS_DEFAULT 4 /* CALL retries before giving up */
160/* CALLs on the fast control mode before the caller escalates to the MFSK
161 * floor; after that it alternates MFSK and the control mode (#235: under
162 * fading an MFSK CALL is no surer than a DATAC16 one at -9..-11 dB, and all-
163 * MFSK lost connects DATAC16 made; MFSK is what connects at -13 dB). A good
164 * link connects exactly as quickly as before. */
165#define ARQ_CALL_FAST_SLOTS 2
166/* After an ACCEPT on MFSK the callee listens this long for the caller's first
167 * round: the caller decodes the ACCEPT ~4 s after it ends, keys one ISS guard
168 * later, and a floor round is a 13.5 s frame the callee decodes ~4 s after. */
169#define ARQ_ACCEPT_RX_WINDOW_FLOOR_MS 26000
170#define ARQ_ACCEPT_RETRY_SLOTS_DEFAULT 4 /* ACCEPT retries before returning */
171#define ARQ_DISCONNECT_RETRY_SLOTS_DEFAULT 2 /* DISCONNECT frame retries */
172
173/* Runtime-configurable retry counts (set via RETRIES TCP command). */
174extern _Atomic int arq_call_retry_slots;
175extern _Atomic int arq_accept_retry_slots;
176extern _Atomic int arq_disconnect_retry_slots;
177
178/* Runtime-configurable CALL/ACCEPT retry interval in seconds (set via CALLINT
179 * TCP command). 0.0 = use compiled default (7.0s). Minimum enforced: 4.0s.
180 * Only affects CALL/ACCEPT retry scheduling during connection setup; the
181 * DISCONNECT uses the immutable table values. */
182#define ARQ_CALLINT_MIN_S 4.0f
183#define ARQ_CALLINT_DEFAULT_S 0.0f /* 0 = use table default */
184extern _Atomic float arq_callint_override_s;
186/* Deterministic retry stagger (ms). The two peers of a session compare the
187 * same pair of exchanged callsigns, so exactly one of them defers by this
188 * fixed offset on every retry — no random draw, no tail. 0 disables the
189 * stagger (an explicit off-switch); tunable via arq:retry_stagger_ms. */
190#define ARQ_RETRY_STAGGER_MS_DEFAULT 2000
191#define ARQ_RETRY_STAGGER_MS_MAX 5000
192extern _Atomic int arq_retry_stagger_ms;
193
194/* Deterministic per-node retry rank from the exchanged callsign pair.
195 * strcmp() is antisymmetric, so the two peers always disagree: one returns 0
196 * (send first) and the other 1 (defer). Returns -1 when the pair is
197 * degenerate (identical or missing callsigns) — a misconfiguration that falls
198 * back to a random draw. */
199int arq_protocol_retry_rank(const char *local_call, const char *remote_call);
200
201uint64_t arq_protocol_retry_deadline_ms(float seconds, int rank);
202
203#define ARQ_CALL_RETRY_SLOTS atomic_load(&arq_call_retry_slots)
204#define ARQ_ACCEPT_RETRY_SLOTS atomic_load(&arq_accept_retry_slots)
205#define ARQ_DISCONNECT_RETRY_SLOTS atomic_load(&arq_disconnect_retry_slots)
207/* The silence between two frames of one keydown (the carousel's poll and its
208 * first burst). The peer is already receiving, so this is not a turnaround:
209 * it only has to let the peer's control decoder report the first frame and
210 * its payload decoder drop any sync taken off it before the next preamble
211 * arrives. Measured, not tuned: see the one-keydown bench. */
212#define ARQ_ACK_DATA_GAP_MS 300
213
214/* Listen before talk. "Busy" is a decoder holding sync on an incoming burst
215 * (arq_update_link_metrics()); HOLD_MS is how stale a sync observation may be
216 * and still count as busy. The modem updates it once per RX chunk, so this
217 * only covers scheduling jitter, not a whole frame: sync keeps being
218 * re-observed for as long as the burst lasts.
220 * A deferred transmission (the DISCONNECT) waits in DEFER_MS steps, at most
221 * DEFER_MAX of them: false sync is real (a payload decoder can latch onto a
222 * control burst), and a decoder stuck in sync must not block a transmission
223 * forever, so after that we key anyway and take the collision. */
224#define ARQ_CHANNEL_SYNC_HOLD_MS 250
225#define ARQ_CHANNEL_DEFER_MS 500
226#define ARQ_CHANNEL_DEFER_MAX 20 /* ~10 s, then key regardless */
227/* Before a DISCONNECT: how long past the longer reply guard to leave for a
228 * frame our last transmission invited to start and be heard, before keying
229 * over the gap. */
230#define ARQ_REPLY_WINDOW_MARGIN_MS 1000
232/* Decode thresholds (dB) of the rungs, which set the carousel's start rung
233 * (carousel.c, car_start_level) and MFSK's place below DATAC15. */
234#define ARQ_SNR_HYST_DB 5.0f
235#define ARQ_SNR_MIN_DATAC15_DB -7.0f /* its cliff (sim, bench): below
236 * it the carousel's MFSK rung wins */
237#define ARQ_SNR_MIN_DATAC4_DB -6.0f /* bench (docs/MODES.md):
238 * DATAC15/DATAC4 goodput crossover
239 * ~-7.5 dB MPP / -8.5 dB AWGN */
240#define ARQ_SNR_MIN_DATAC3_DB -1.0f
241#define ARQ_SNR_MIN_DATAC1_DB 3.0f
242#define ARQ_SNR_MIN_DATAC17_DB 7.0f /* bench (docs/MODES.md): goodput
243 * crossover vs DATAC1 ~ +6 dB MPP;
244 * 89/100 delivery at +8 */
245#define ARQ_SNR_MIN_QAM16C2_DB 13.0f /* bench: goodput crossover vs
246 * DATAC17 ~ +11 dB MPP, 84/100 at
247 * +13.7; upstream's published
248 * operating point is +15 */
249
250/* Absolute cap on how long an APP_DISCONNECT may stay deferred while the FSM
251 * tries to drain the last app bytes. Once the application has asked to
252 * disconnect, the deferral must always resolve into a clean air-side
253 * DISCONNECT handshake within this window — otherwise a stuck session keeps
254 * keying the rig forever (the K7EK "Mercury kept hanging on" report). On a
255 * healthy link the drain completes in seconds, so this only bites when the
256 * link is failing. */
257#define ARQ_DISCONNECT_DRAIN_TIMEOUT_S_DEFAULT 45
258extern _Atomic int arq_disconnect_drain_timeout_s;
259#define ARQ_DISCONNECT_DRAIN_TIMEOUT_S atomic_load(&arq_disconnect_drain_timeout_s)
260
261/* Mode table (defined in arq_protocol.c) */
262extern const arq_mode_timing_t arq_mode_table[];
263extern const int arq_mode_table_count;
264
265/* ======================================================================
266 * Frame codec API
267 * ====================================================================== */
268
273int arq_protocol_encode_hdr(uint8_t *buf, size_t buf_len, const arq_frame_hdr_t *hdr);
274
279int arq_protocol_decode_hdr(const uint8_t *buf, size_t buf_len, arq_frame_hdr_t *hdr);
280
286
291int arq_protocol_bw_hz_from_token(uint8_t bw_token);
292
297uint8_t arq_protocol_encode_snr(float snr_db);
298
303float arq_protocol_decode_snr(uint8_t snr_raw);
304
309const arq_mode_timing_t *arq_protocol_mode_timing(int freedv_mode);
310
320
326/* The wait after a CALL on this carrier. After an MFSK CALL: the callee
327 * decodes it ~4 s after it ends, answers one guard later with a 13.5 s MFSK
328 * ACCEPT, which this end decodes ~4 s after -- about 24 s, where the table's
329 * DATAC16-shaped interval would key the next CALL over the answer. */
331
336uint16_t arq_protocol_callsign_crc16(const char *callsign);
337
338/* ======================================================================
339 * Frame builder API
341 * Each function fills `buf` (caller-provided) with a complete ready-to-TX
342 * frame (framer byte + header/payload) and returns the total byte count,
343 * or -1 if buf_len < required size or arguments are invalid. Control frames
344 * are padded to ARQ_CONTROL_FRAME_SIZE (14 bytes) on the air.
345 * ====================================================================== */
348int arq_protocol_build_disconnect(uint8_t *buf, size_t buf_len,
349 uint8_t session_id, uint8_t snr_raw);
350
351/* --- CALL/ACCEPT, protocol version 1 (PACKET_TYPE_ARQ_CALL) ---
352 *
353 * 14 bytes (a DATAC16 frame), padded to the frame on MFSK. Byte 0 is the
354 * framer: type CALL, and in its extension the bandwidth token (2 bits), the
355 * protocol version (2 bits) and the carousel bit. A station takes a frame
356 * only with this version and the carousel bit; a 1.9.x station reads that
357 * extension as an unknown bandwidth token and drops it, so the versions do
358 * not connect (docs/CAROUSEL-ARQ.md, "Wire formats").
359 *
360 * CALL 1: 0 | session id 2-3: CRC16 of the called callsign
361 * 4-13: the caller's callsign, arithmetic-coded
362 * ACCEPT 1: 1 | session id 2-3: CRC16 of the caller's callsign
363 * 4-5: CRC16 of the called callsign 6-7: the callee's nonce
364 * 8: the caller's start rung (3 bits; 7 = ARQ_ACCEPT_LEVEL_CTL_DEAF),
365 * 5 bits zero 9-13: zero */
366#define ARQ_CONNECT_VERSION 1
367#define ARQ_CONNECT_EXT_BW_MASK 0x03
368#define ARQ_CONNECT_EXT_VERSION_SHIFT 2
369#define ARQ_CONNECT_EXT_VERSION_MASK 0x0C
370#define ARQ_CONNECT_EXT_CAROUSEL 0x10
371#define ARQ_ACCEPT_CALLED_CRC_IDX 4
372#define ARQ_ACCEPT_NONCE_IDX 6
373#define ARQ_ACCEPT_LEVEL_IDX 8
374#define ARQ_ACCEPT_LEVEL_CTL_DEAF 7 /* the callee hears the caller below
375 the control mode: start at the floor */
378int arq_protocol_build_call(uint8_t *buf, size_t buf_len,
379 uint8_t session_id,
380 const char *caller, const char *called,
381 int bw_hz);
382
386int arq_protocol_build_accept(uint8_t *buf, size_t buf_len,
387 uint8_t session_id,
388 const char *called, const char *caller,
389 int bw_hz, int start_level, uint16_t nonce);
390
392bool arq_protocol_connect_version_ok(const uint8_t *buf, size_t buf_len);
393
395bool arq_protocol_connect_dst_matches(const uint8_t *buf, const char *callsign);
396
399int arq_protocol_parse_call(const uint8_t *buf, size_t buf_len,
400 uint8_t *session_id_out, char *caller_out,
401 int *bw_hz_out);
402
403typedef struct {
404 uint8_t session_id;
405 int bw_hz;
406 uint16_t called_crc; /* CRC16 of the callsign the caller called */
407 uint16_t nonce; /* the callee's session nonce */
408 int start_level; /* 0..6, or ARQ_ACCEPT_LEVEL_CTL_DEAF */
410
413int arq_protocol_parse_accept(const uint8_t *buf, size_t buf_len, arq_accept_t *out);
414
418int arq_protocol_build_cq(uint8_t *buf, size_t buf_len,
419 const char *src, int bw_hz);
420
424int arq_protocol_parse_cq(const uint8_t *buf, size_t buf_len,
425 char *src_out, int *bw_hz_out);
426
427#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:216
int arq_protocol_parse_cq(const uint8_t *buf, size_t buf_len, char *src_out, int *bw_hz_out)
Parse a compact DATAC16 CQ frame.
Definition arq_protocol.c:542
_Atomic int arq_disconnect_retry_slots
Definition arq_protocol.c:21
_Atomic float arq_callint_override_s
Definition arq_protocol.c:85
int arq_protocol_parse_accept(const uint8_t *buf, size_t buf_len, arq_accept_t *out)
Parse an ACCEPT.
Definition arq_protocol.c:495
_Atomic int arq_retry_stagger_ms
Definition arq_protocol.c:22
int arq_protocol_build_accept(uint8_t *buf, size_t buf_len, uint8_t session_id, const char *called, const char *caller, int bw_hz, int start_level, uint16_t nonce)
Build an ACCEPT.
Definition arq_protocol.c:451
uint16_t arq_protocol_callsign_crc16(const char *callsign)
Compute CRC16-CCITT of an uppercase-normalised callsign.
Definition arq_protocol.c:280
float arq_protocol_longest_burst_s(void)
Longest single-burst airtime across the whole mode table, in seconds.
Definition arq_protocol.c:97
_Atomic int arq_accept_retry_slots
Definition arq_protocol.c:20
const int arq_mode_table_count
Definition arq_protocol.c:74
_Atomic int arq_call_retry_slots
Definition arq_protocol.c:19
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:87
int arq_protocol_parse_call(const uint8_t *buf, size_t buf_len, uint8_t *session_id_out, char *caller_out, int *bw_hz_out)
Parse a CALL: session id, the caller's callsign, the bandwidth.
Definition arq_protocol.c:478
float arq_protocol_call_interval_s(void)
Return the CALL/ACCEPT retry interval in seconds, applying any CALLINT override.
Definition arq_protocol.c:110
int arq_protocol_build_disconnect(uint8_t *buf, size_t buf_len, uint8_t session_id, uint8_t snr_raw)
DISCONNECT frame (PACKET_TYPE_ARQ_CONTROL).
Definition arq_protocol.c:261
int arq_protocol_build_call(uint8_t *buf, size_t buf_len, uint8_t session_id, const char *caller, const char *called, int bw_hz)
Build a CALL.
Definition arq_protocol.c:429
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:201
uint64_t arq_protocol_retry_deadline_ms(float seconds, int rank)
Definition arq_protocol.c:142
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:238
int arq_protocol_retry_rank(const char *local_call, const char *remote_call)
Definition arq_protocol.c:132
_Atomic int arq_disconnect_drain_timeout_s
Definition arq_protocol.c:23
int arq_protocol_build_cq(uint8_t *buf, size_t buf_len, const char *src, int bw_hz)
Build a compact DATAC16 CQ frame carrying source callsign and BW token.
Definition arq_protocol.c:518
bool arq_protocol_connect_dst_matches(const uint8_t *buf, const char *callsign)
Is a CALL or ACCEPT addressed to callsign (bytes 2-3, all 16 bits)?
Definition arq_protocol.c:473
const arq_mode_timing_t arq_mode_table[]
Definition arq_protocol.c:57
float arq_protocol_call_interval_for_mode_s(int mode)
Definition arq_protocol.c:120
bool arq_protocol_connect_version_ok(const uint8_t *buf, size_t buf_len)
Is buf a CALL or ACCEPT of this protocol version?
Definition arq_protocol.c:414
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:184
float arq_protocol_decode_snr(uint8_t snr_raw)
Decode snr_raw wire byte back to float dB.
Definition arq_protocol.c:249
arq_subtype_t
Definition arq_protocol.h:82
@ ARQ_SUBTYPE_DISCONNECT
Definition arq_protocol.h:83
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:168
int mode
Definition carousel.c:2330
int bw_hz
Definition carousel.c:2369
Definition arq_protocol.h:374
Definition arq_protocol.h:91
uint8_t packet_type
Definition arq_protocol.h:92
uint8_t session_id
Definition arq_protocol.h:96
uint8_t frame_ext
Definition arq_protocol.h:93
uint8_t flags
Definition arq_protocol.h:95
uint8_t tx_seq
Definition arq_protocol.h:97
uint8_t subtype
Definition arq_protocol.h:94
uint8_t ack_delay_raw
Definition arq_protocol.h:100
uint8_t snr_raw
Definition arq_protocol.h:99
uint8_t rx_ack_seq
Definition arq_protocol.h:98
Definition arq_protocol.h:111
float retry_interval_s
Definition arq_protocol.h:114
int payload_bytes
Definition arq_protocol.h:115
float frame_duration_s
Definition arq_protocol.h:113
int freedv_mode
Definition arq_protocol.h:112